package qldt

import (
	"bytes"
	"context"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"strings"
	"time"
)

type CourseItem struct {
	ID         int64  `json:"id"`
	Name       string `json:"name"`
	CourseCode string `json:"courseCode"`
}

type CoursesResponse struct {
	Data       []CourseItem `json:"data"`
	Message    string       `json:"message"`
	StatusCode int          `json:"statusCode"`
}

type AttendanceStudent struct {
	StudentID int64 `json:"student_id"`
	Status    int   `json:"status"`
}

type AttendancePayload struct {
	UsersID   int     `json:"users_id"`
	CoursesID int     `json:"courses_id"`
	Period    int     `json:"period"`
	Students  []AttendanceStudent `json:"students"`
	ClassID   int64   `json:"class_id"`
	Date      string  `json:"date"`
	Type      string  `json:"type"`
}

func ParseUserIDFromToken(token string) (int, error) {
	parts := strings.Split(token, ".")
	if len(parts) < 2 {
		return 0, fmt.Errorf("invalid jwt")
	}
	payload, err := base64.RawURLEncoding.DecodeString(parts[1])
	if err != nil {
		return 0, err
	}
	var claims struct {
		ID int `json:"id"`
	}
	if err := json.Unmarshal(payload, &claims); err != nil {
		return 0, err
	}
	if claims.ID == 0 {
		return 0, fmt.Errorf("token missing user id")
	}
	return claims.ID, nil
}

func (c *Client) GetClassCourses(ctx context.Context, token string, classID int64) (*CoursesResponse, error) {
	u := fmt.Sprintf("%s/courses/class/%d", c.baseURL, classID)
	req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil)
	if err != nil {
		return nil, err
	}
	c.setAuthHeaders(req, token)

	resp, err := c.http.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	body, _ := io.ReadAll(resp.Body)
	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		return nil, fmt.Errorf("qldt courses http %d: %s", resp.StatusCode, string(body))
	}

	var out CoursesResponse
	if err := json.Unmarshal(body, &out); err != nil {
		return nil, err
	}
	if out.StatusCode == 401 || strings.Contains(strings.ToLower(out.Message), "expired") {
		return nil, fmt.Errorf("qldt token invalid: %s", out.Message)
	}
	return &out, nil
}

func (c *Client) SaveAttendance(ctx context.Context, token string, payload *AttendancePayload) ([]byte, int, error) {
	jsonData, err := json.Marshal(payload)
	if err != nil {
		return nil, 0, err
	}

	u := c.baseURL + "/attendance"
	req, err := http.NewRequestWithContext(ctx, http.MethodPost, u, bytes.NewBuffer(jsonData))
	if err != nil {
		return nil, 0, err
	}
	req.Header.Set("Content-Type", "application/json")
	c.setAuthHeaders(req, token)

	resp, err := c.http.Do(req)
	if err != nil {
		return nil, 0, err
	}
	defer resp.Body.Close()

	body, _ := io.ReadAll(resp.Body)
	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		return body, resp.StatusCode, fmt.Errorf("qldt attendance post %d: %s", resp.StatusCode, string(body))
	}
	return body, resp.StatusCode, nil
}

func (c *Client) UpdateAttendanceDetailStatus(ctx context.Context, token string, detailID int64, status string) error {
	payload := map[string]string{"status": status}
	jsonData, _ := json.Marshal(payload)

	u := fmt.Sprintf("%s/attendance-detail/status/%d", c.baseURL, detailID)
	req, err := http.NewRequestWithContext(ctx, http.MethodPatch, u, bytes.NewBuffer(jsonData))
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", "application/json")
	c.setAuthHeaders(req, token)

	resp, err := c.http.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		body, _ := io.ReadAll(resp.Body)
		return fmt.Errorf("qldt patch detail %d: %s", resp.StatusCode, string(body))
	}
	return nil
}

type PortalAttendanceDetail struct {
	ID     int64  `json:"id"`
	Status string `json:"status"`
	Student struct {
		ID int64 `json:"id"`
	} `json:"student"`
}

type PortalAttendanceSession struct {
	ID               int64                    `json:"id"`
	Date             string                   `json:"date"`
	Period           int                      `json:"period"`
	AttendanceDetail []PortalAttendanceDetail `json:"attendanceDetail"`
}

type PortalAttendanceResponse struct {
	Data    map[string][]PortalAttendanceSession `json:"data"`
	Message string                               `json:"message"`
}

func (c *Client) GetAttendance(ctx context.Context, token string, classID int64, courseID int64) (*PortalAttendanceResponse, error) {
	u, _ := url.Parse(c.baseURL + "/attendance")
	q := u.Query()
	q.Set("class_id", fmt.Sprintf("%d", classID))
	q.Set("course_id", fmt.Sprintf("%d", courseID))
	u.RawQuery = q.Encode()

	req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil)
	if err != nil {
		return nil, err
	}
	c.setAuthHeaders(req, token)

	resp, err := c.http.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	body, _ := io.ReadAll(resp.Body)
	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		return nil, fmt.Errorf("qldt get attendance %d: %s", resp.StatusCode, string(body))
	}

	var out PortalAttendanceResponse
	if err := json.Unmarshal(body, &out); err != nil {
		return nil, err
	}
	return &out, nil
}

func (c *Client) setAuthHeaders(req *http.Request, token string) {
	req.Header.Set("Authorization", "Bearer "+token)
	if c.origin != "" {
		req.Header.Set("Origin", c.origin)
		req.Header.Set("Referer", c.origin+"/")
	}
	req.Header.Set("Accept", "application/json")
}

func AttendanceStatusLabel(status int) string {
	switch status {
	case 1:
		return "Nghỉ có phép"
	case 2:
		return "Nghỉ nửa buổi"
	case 3:
		return "Đi học muộn"
	case 4:
		return "Đi học đầy đủ"
	default:
		return "Nghỉ không phép"
	}
}

func ShiftDurationMinutes(start, end string) int {
	return parseHM(end) - parseHM(start)
}

func parseHM(t string) int {
	parts := strings.Split(t, ":")
	if len(parts) < 2 {
		return 0
	}
	h, m := 0, 0
	fmt.Sscanf(parts[0], "%d", &h)
	fmt.Sscanf(parts[1], "%d", &m)
	return h*60 + m
}

func InferStatusFromOnlineMinutes(onlineMins, shiftMins int) int {
	if shiftMins <= 0 {
		if onlineMins > 0 {
			return 4
		}
		return 0
	}
	rate := float64(onlineMins) / float64(shiftMins)
	switch {
	case rate >= 0.85:
		return 4
	case rate >= 0.5:
		return 2
	case onlineMins > 0:
		return 3
	default:
		return 0
	}
}

func NowDateVN() string {
	return time.Now().Format("2006-01-02")
}
