/**
 * Core data models for the Progress Tracker system
 */
import {ucFirst} from "../utils/ProgressTrackerUtils";


export interface GradeSchemeDto
{
    Ref: string;
    Name: string;
}

export interface GradeDto
{
    Id: number;
    Ref: string;
    IsScored: boolean;
    LowerPercent: number | null;
    UpperPercent: number | null;
    IsCriteria: boolean;
    TryCount: number | null;
    IsReferred: boolean | null;
    IsFail: boolean | null;
}

export class GradeWithSchemeDto
{
    constructor(
        public Id: number,
        public Ref: string,
        public SchemeName: string,
        public SchemeRef: string,
        public IsScored: boolean,
        public LowerPercent: number | null,
        public UpperPercent: number | null,
        public IsCriteria: boolean,
        public TryCount: number | null,
        public IsReferred: boolean | null,
        public IsFail: boolean | null
    )
    {}

    static fromJSON(json: any): GradeWithSchemeDto
    {
        return Object.assign(Object.create(GradeWithSchemeDto.prototype), json);
    }
}

// Basic tracker information
export class Tracker
{
    constructor(
        public id: number,
        public name: string,
        public qualificationId: number,
        public moduleId: number
    )
    {}

    static fromJSON(json: any): Tracker
    {
        return new Tracker(
            json.id || 0,
            json.name || '',
            json.qualificationId || 0,
            json.moduleId || 0
        );
    }
}

// Category represents a type of part (e.g., QUALIFICATION, UNIT, etc.)
export class Category
{
    constructor(
        public ref: string,
        public name: string
    )
    {
    }

    static fromJSON(json: any): Category
    {
        return new Category(
            json.ref || '',
            json.name || ''
        );
    }
}


// A part in the tracker hierarchy (could be a qualification, unit, etc.)
export class TrackerPart
{
    constructor(
        public trackerPartId: number,
        public partId: number,
        public reference: string,
        public name: string,
        public category: string,
        public parent: number | null,
        public calculationRulePreset: string,
        public gradeScheme: string
    )
    {}    
    
    static fromJSON(json: any): TrackerPart
    {
        /** See PHP: {{@see TrackerPartDto}} */
        return new TrackerPart(
            json.trackerPartId || 0,
            json.partId,
            json.reference || '',
            json.name || '',
            json.category || '',
            json.parentTrackerPartId,
            json.calculationRulePreset ||  '',
            json.gradeScheme || ''
        );

    }

    // Create a special "Show All" option for a category
    static createShowAllOption(category: string, categoryName: string): TrackerPart
    {
        return new TrackerPart(
            -1, // Special ID for "Show All" option
            -1,
            `ALL_${category}`,
            `Show All ${ucFirst(categoryName)}(s)`,
            category,
            -1,
            '',
            ''
        );
    }
}

// GUI configuration for a part
export class PartGui
{
    constructor(
        public partId: number,
        public ref: string,
        public name: string,
        public gradeCol: "VISIBLE" | "EDITABLE" | "HIDDEN",
        public scoreCol: "VISIBLE" | "EDITABLE" | "HIDDEN",
        public unitCriteria: "VISIBLE" | "EDITABLE" | "HIDDEN",
        public showChildJumpButton: boolean,
        public showChildCriteria: boolean,
        public cellSummaryResultMetric?: "grade" | "score"
    )
    {}

    static fromJSON(json: any): PartGui
    {
        return new PartGui(
            json.partId || 0,
            json.reference || '',
            json.name || '',
            json.gradeCol || 'HIDDEN',
            json.scoreCol || 'HIDDEN',
            json.unitCriteria || 'HIDDEN',
            json.showChildJumpButton || false,
            json.showChildCriteria || false,
            json.cellSummaryResultMetric
        );
    }
}

// Student information
export class Student
{
    constructor(
        public id: number,
        public fullName: string,
        public targetGrade: string | null = null,
        public reference: string = '',
        public callouts: string[] = [],
        public pronouns: string = '',
        public links: { rel: string; href: string; type: string; title?: string }[] = []
    )
    {}

    static fromJSON(json: any): Student
    {
        return new Student(
            json.id || 0,
            json.fullName || '',
            json.targetGrade || null,
            json.reference || '',
            json.callouts || [],
            json.pronouns || '',
            json.links || []
        );
    }
}

// Criterion state (pass/refer/fail)
export class CriterionState
{
    constructor(
        public attained: boolean,
        public referred: boolean,
        public failed: boolean,
        public attemptNumber: number // Changed from 'try' to 'attemptNumber' to avoid the reserved keyword
    )
    {
    }

    static fromJSON(json: any): CriterionState | null
    {
        if (!json) return null;
        return new CriterionState(
            json.attained || false,
            json.referred || false,
            json.failed || false,
            json.try || json.attemptNumber || 1 // Handle both property names for backward compatibility
        );
    }

    // Predefined states
    static get ATTAINED_1(): CriterionState
    {
        return new CriterionState(true, false, false, 1);
    }

    static get REFERRED_1(): CriterionState
    {
        return new CriterionState(false, true, false, 1);
    }

    static get ATTAINED_2(): CriterionState
    {
        return new CriterionState(true, false, false, 2);
    }

    static get REFERRED_2(): CriterionState
    {
        return new CriterionState(false, true, false, 2);
    }

    static get FAILED(): CriterionState
    {
        return new CriterionState(false, false, true, 2);
    }

    // Get the cycle sequence
    static getCycleStates(): (CriterionState | null)[]
    {
        return [
            CriterionState.ATTAINED_1,
            CriterionState.REFERRED_1,
            CriterionState.ATTAINED_2,
            CriterionState.REFERRED_2,
            CriterionState.FAILED,
            null
        ];
    }

    // Get the next state in the cycle
    static getNextState(currentState: CriterionState | null): CriterionState | null
    {
        const states = CriterionState.getCycleStates();
        let currentIndex = states.length - 1; // Default to last (blank)

        if (currentState) {
            const foundIndex = states.findIndex(
                (state) =>
                    state !== null &&
                    state.attained === currentState.attained &&
                    state.referred === currentState.referred &&
                    state.failed === currentState.failed &&
                    state.attemptNumber === currentState.attemptNumber
            );

            if (foundIndex !== -1) {
                currentIndex = foundIndex;
            }
        }

        // Return next state in the cycle
        const nextIndex = (currentIndex + 1) % states.length;
        return states[nextIndex];
    }
}

// todo pt Rename "Mark" to "Result".
// Mark data for a student
export class MarkData
{
    constructor(
        public grade: string = '',
        public score: number | null = null,
        public criteria: { [key: string]: CriterionState | null } = {}
    )
    {}

    static fromJSON(json: any): MarkData
    {
        const markData = new MarkData(
            json.grade || '',
            json.score !== undefined ? json.score : null,
            {}
        );

        // Convert criteria if present
        if (json.criteria) {
            Object.keys(json.criteria).forEach(key =>
            {
                markData.criteria[key] = CriterionState.fromJSON(json.criteria[key]);
            });
        }

        return markData;
    }
}

// Details about calculation rules
export class RuleDetail
{
    constructor(
        public name: string,
        public description: string,
        public criteria: { [key: string]: any } = {}
    )
    {
    }

    static fromJSON(json: any): RuleDetail
    {
        return new RuleDetail(
            json.name || '',
            json.description || '',
            json.criteria || {}
        );
    }
}

// Filter hierarchy information
export class FilterHierarchy
{
    constructor(
        public category: string,
        public level: number,
        public parentCategory: string | null,
        public categoryName: string,
        public isRoot: boolean
    )
    {}

    static fromJSON(json: any): FilterHierarchy
    {
        return new FilterHierarchy(
            json.category,
            json.level,
            json.parentCategory,
            json.categoryName,
            json.isRoot
        );
    }
}

// Part representation for PTMarkScreen
export class Part
{
    constructor(
        public id: number,
        public name: string,
        public ref: string,
        public guiConfig: PartGui
    )
    {}

    static fromTrackerPart(part: TrackerPart, guiConfig: PartGui): Part
    {
        return new Part(
            part.trackerPartId,
            part.name,
            part.reference,
            guiConfig
        );
    }
}
