package qldt

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

type Client struct {
	baseURL string
	origin  string
	http    *http.Client
}

func NewClient(baseURL, origin string) *Client {
	baseURL = strings.TrimRight(baseURL, "/")
	return &Client{
		baseURL: baseURL,
		origin:  origin,
		http: &http.Client{
			Timeout: 45 * time.Second,
		},
	}
}

type StudentsResponse struct {
	Message    string       `json:"message"`
	StatusCode int          `json:"statusCode"`
	Data       []StudentDTO `json:"data"`
	Total      int          `json:"total"`
	Page       string       `json:"page"`
	PageSize   string       `json:"pageSize"`
}

type StudentDTO struct {
	ID          int64   `json:"id"`
	StudentCode string  `json:"studentCode"`
	FullName    string  `json:"fullName"`
	Phone       *string `json:"phone"`
	Email       string  `json:"email"`
	DateOfBirth *string `json:"dateOfBirth"`
	Gender      *int    `json:"gender"`
	Status      *string `json:"status"`
	Location    *string `json:"location"`
	Avatar      *string `json:"avatar"`
	System      *struct {
		ID   int64  `json:"id"`
		Name string `json:"name"`
	} `json:"system"`
}

type ClassesResponse struct {
	Data       []ClassDTO `json:"data"`
	StatusCode int        `json:"statusCode"`
}

type ClassDTO struct {
	ID           int64   `json:"id"`
	Name         string  `json:"name"`
	ClassCode    string  `json:"classCode"`
	Type         string  `json:"type"`
	StudentCount int     `json:"studentCount"`
	CreatedAt    string  `json:"createdAt"`
	UpdatedAt    *string `json:"updatedAt"`
	Specializes  *struct {
		ID   int64  `json:"id"`
		Name string `json:"name"`
		Systems *struct {
			ID         int64  `json:"id"`
			SystemCode string `json:"systemCode"`
			Name       string `json:"name"`
		} `json:"systems"`
	} `json:"specializes"`
	Courses []classCourseNestDTO `json:"courses"`
}

type classCourseNestDTO struct {
	ID   int64  `json:"id"`
	Name string `json:"name"`
}

func (c *Client) GetStudents(ctx context.Context, token string, page, pageSize int) (*StudentsResponse, error) {
	u, err := url.Parse(c.baseURL + "/students")
	if err != nil {
		return nil, err
	}
	q := u.Query()
	q.Set("page", strconv.Itoa(page))
	q.Set("pageSize", strconv.Itoa(pageSize))
	u.RawQuery = q.Encode()

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

	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")

	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 students http %d: %s", resp.StatusCode, string(body))
	}

	var out StudentsResponse
	if err := json.Unmarshal(body, &out); err != nil {
		return nil, err
	}
	if out.StatusCode != 0 && out.StatusCode != 200 {
		return nil, fmt.Errorf("qldt statusCode %d: %s", out.StatusCode, out.Message)
	}

	return &out, nil
}

func (c *Client) GetClassesBySystem(ctx context.Context, token string, systemID int64) (*ClassesResponse, error) {
	u := fmt.Sprintf("%s/classes/system/%d", c.baseURL, systemID)
	req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil)
	if err != nil {
		return nil, err
	}
	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")

	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 classes/system/%d http %d: %s", systemID, resp.StatusCode, string(body))
	}

	var outClasses ClassesResponse
	if err := json.Unmarshal(body, &outClasses); err != nil {
		return nil, err
	}
	if outClasses.StatusCode != 0 && outClasses.StatusCode != 200 {
		return nil, fmt.Errorf("qldt classes statusCode %d", outClasses.StatusCode)
	}
	return &outClasses, nil
}

func (c *Client) GetClassDashboard(ctx context.Context, token string, classID, courseID int64) ([]byte, error) {
	u := fmt.Sprintf("%s/classes/dashboard/%d/%d", c.baseURL, classID, courseID)
	req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil)
	if err != nil {
		return nil, err
	}
	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")

	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 classes/dashboard/%d/%d http %d: %s", classID, courseID, resp.StatusCode, string(body))
	}

	var probe struct {
		StatusCode int    `json:"statusCode"`
		Message    string `json:"message"`
	}
	if err := json.Unmarshal(body, &probe); err == nil {
		if probe.StatusCode != 0 && probe.StatusCode != 200 {
			return nil, fmt.Errorf("qldt classes/dashboard statusCode %d: %s", probe.StatusCode, probe.Message)
		}
	}
	return body, nil
}
