//Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format.
//Copyright (C) 2024 HTWKalender support@htwkalender.de

//This program is free software: you can redistribute it and/or modify
//it under the terms of the GNU Affero General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
//(at your option) any later version.

//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU Affero General Public License for more details.

//You should have received a copy of the GNU Affero General Public License
//along with this program.  If not, see <https://www.gnu.org/licenses/>.

package v1

import (
	"encoding/xml"
	"fmt"
	"github.com/pocketbase/pocketbase"
	"htwkalender/data-manager/model"
	"htwkalender/data-manager/service/db"
	"htwkalender/data-manager/service/functions"
	"htwkalender/data-manager/service/functions/time"
	"io"
	"log/slog"
	"net/http"
)

func getSeminarHTML(semester string) (string, error) {
	url := "https://stundenplan.htwk-leipzig.de/stundenplan/xml/public/semgrp_" + semester + ".xml"

	// Send GET request
	response, err := http.Get(url)
	if err != nil {
		fmt.Printf("Error occurred while making the request: %s\n", err.Error())
		return "", err
	}
	defer func(Body io.ReadCloser) {
		err := Body.Close()
		if err != nil {
			return
		}
	}(response.Body)

	// Read the response body
	body, err := io.ReadAll(response.Body)

	if err != nil {
		fmt.Printf("Error occurred while reading the response: %s\n", err.Error())
		return "", err
	}

	return string(body), err

}

func FetchSeminarGroups(base *pocketbase.PocketBase) (model.SeminarGroups, error) {
	var groups model.SeminarGroups

	semesterString := functions.CalculateSemesterList(time.RealClock{})
	var results [2]string
	var err error

	for i, semester := range semesterString {
		results[i], err = getSeminarHTML(semester)
		if err != nil {
			slog.Error("Error while fetching seminar groups for: "+semester, "error", err)
			return nil, err
		}
		groups = append(groups, parseSeminarGroups(results[i], semester)...)
	}

	// filter duplicates
	groups = removeDuplicates(groups)

	insertedGroups, dbError := db.SaveGroups(base.App, groups)
	if dbError != nil {
		slog.Error("FetchSeminarGroups", "error", dbError)
		return nil, err
	}

	return insertedGroups, nil
}

func removeDuplicates(groups model.SeminarGroups) model.SeminarGroups {
	uniqueGroups := make(model.SeminarGroups, 0, len(groups))
	seen := make(map[string]struct{}) // Use an empty struct to minimize memory usage

	// unique Identifier is the course and semester
	for _, group := range groups {
		key := group.UniqueKey()
		if _, exists := seen[key]; !exists {
			seen[key] = struct{}{}
			uniqueGroups = append(uniqueGroups, group)
		}
	}
	return uniqueGroups
}

func contains(groups []model.SeminarGroup, group model.SeminarGroup) bool {
	for _, a := range groups {
		if (a.Course == group.Course) && (a.Semester == group.Semester) {
			return true
		}
	}
	return false
}

func parseSeminarGroups(result string, semester string) model.SeminarGroups {

	var studium model.Studium
	err := xml.Unmarshal([]byte(result), &studium)
	if err != nil {
		return nil
	}

	var seminarGroups model.SeminarGroups
	for _, faculty := range studium.Faculty {
		for _, Studiengang := range faculty.Studiengang {
			for _, Studienrichtung := range Studiengang.Semgrp {
				seminarGroup := model.SeminarGroup{
					University:    "HTWK-Leipzig",
					GroupShortcut: Studiengang.Name,
					GroupId:       Studiengang.ID,
					Course:        Studienrichtung.Name,
					Faculty:       faculty.Name,
					FacultyId:     faculty.ID,
					Semester:      semester,
				}
				seminarGroups = append(seminarGroups, seminarGroup)
			}
		}
	}
	return seminarGroups
}
