import
{
    Tracker,
    TrackerPart,
    PartGui,
    Student,
    MarkData,
    GradeWithSchemeDto,
    RuleDetail,
} from '../models/ProgressTrackerModels.js';

interface ApiConfig
{
    baseUrl: string;
    useMockData?: boolean;
}

export class ProgressTrackerAPI
{
    private readonly baseUrl: string;
    private readonly useMockData: boolean;

    constructor(config: ApiConfig)
    {
        this.baseUrl = config.baseUrl;
        this.useMockData = config.useMockData || false;
    }

    public async fetchTracker(trackerId: number): Promise<Tracker>
    {
        const path = `/pt/trackers/${trackerId}`;
        return Tracker.fromJSON(await this.apiGet(path));
    }

    public async fetchTrackerParts(trackerId: number): Promise<TrackerPart[]>
    {
        const path = `/pt/trackers/${trackerId}/parts`;
        return (await this.apiGet(path)).map((item: any) => TrackerPart.fromJSON(item));
    }

    public async fetchPartGuis(trackerId: number): Promise<PartGui[]>
    {
        const path = `/pt/trackers/${trackerId}/partGuis`;
        return (await this.apiGet(path)).map((item: any) => PartGui.fromJSON(item));

    }

    public async fetchModuleStudents(moduleId: number): Promise<Student[]>
    {
        const path = `/modules/${moduleId}/students`;
        return (await this.apiGet(path)).map((item: any) => Student.fromJSON(item));
    }

    public async fetchGradeData(): Promise<GradeWithSchemeDto[]>
    {
        const path = `/pt/gradeSchemes`;
        return (await this.apiGet(path)).map((item: any) => GradeWithSchemeDto.fromJSON(item));
    }

    // todo pt Rename "Mark" to "Result"
    public async fetchMarkData(trackedPartId: number): Promise<{ [studentId: number]: MarkData }>
    {
        const path = `/pt/trackerParts/${trackedPartId}/resultsAndChildCriteria`;

        const result: { [studentId: number]: MarkData } = {};
        Object.entries((await this.apiGet(path))).forEach(([studentId, markData]: [string, any]) => {
            result[parseInt(studentId)] = MarkData.fromJSON(markData);
        });
        return result;
    }

    public async saveMarkData(
        trackedPartId: number,
        markData: { [studentId: string]: MarkData }
    ): Promise<{ success: boolean; message: string }>
    {
        const path = `/pt/trackerParts/${trackedPartId}/results`;
        return this.apiPut(path, markData);
    }

    public async fetchRuleDetails(rulePreset: string): Promise<RuleDetail>
    {
        const path = `/pt/rulePresets/${encodeURIComponent(rulePreset)}`;
        return RuleDetail.fromJSON(this.apiGet(path));
    }

    private async apiGet(path: string): Promise<any> // Returns JSON i.e. an object, array or literal
    {
        const resp = await fetch(this.baseUrl + path);
        if (!resp.ok) {
            const errorBody = await resp.text();
            throw new Error(`API request failed with status ${resp.status}: ${errorBody || resp.statusText}`);
        }
        return resp.json();
    }

    private async apiPut(path: string, jsonData: any): Promise<any>
    {
        console.log('apiPut', path);
        const resp = await fetch(
            this.baseUrl + path,
            {
                method: 'PUT',
                body: JSON.stringify(jsonData),
            }
        );

        if (!resp.ok) {
            const errorBody = await resp.text();
            throw new Error(`API request failed with status ${resp.status}: ${errorBody || resp.statusText}`);
        }
        return resp.json();
    }
}
