package service

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"os"
	"sort"
	"strings"
	"time"

	"github.com/tidbcloud/aisupport/services/oncall/db"
	"github.com/tidbcloud/aisupport/services/oncall/models"
)

type PrivilageRequest struct {
	Name      string   `json:"name"`
	ExpiredAt int64    `json:"expired_at"`
	Oncallers []string `json:"oncallers"`
}

type PrivilegePreviewRow struct {
	TeamID          uint   `json:"team_id"`
	TeamDisplayName string `json:"team_display_name"`
	TeamKey         string `json:"team_key"`
	EngineerName    string `json:"engineer_name"`
	EngineerEmail   string `json:"engineer_email"`
	ExpiredAt       int64  `json:"expired_at"`
}

type PrivilegePreviewBatch struct {
	Request PrivilageRequest      `json:"request"`
	Rows    []PrivilegePreviewRow `json:"rows"`
}

type PrivilegePreviewResult struct {
	GroupName   string                  `json:"group_name"`
	GeneratedAt int64                   `json:"generated_at"`
	Batches     []PrivilegePreviewBatch `json:"batches"`
}

type PrivilegeSendResult struct {
	Request      PrivilageRequest `json:"request"`
	StatusCode   int              `json:"status_code"`
	ResponseBody string           `json:"response_body"`
}

type PrivilegeSendSummary struct {
	Preview *PrivilegePreviewResult `json:"preview"`
	Results []PrivilegeSendResult   `json:"results"`
}

func getPrivilegeURL() string {
	if v := strings.TrimSpace(os.Getenv("ONCALL_PRIVILEGE_URL")); v != "" {
		return v
	}
	return strings.TrimSpace(os.Getenv("DEVOPS_URL"))
}

func getPrivilegeAPIKey() string {
	if v := strings.TrimSpace(os.Getenv("ONCALL_PRIVILEGE_API_KEY")); v != "" {
		return v
	}
	return strings.TrimSpace(os.Getenv("DEVOPS_API_KEY"))
}

func (s *ScheduleService) BuildPrivilegePreview(groupName string, atTime int64, excludeTestTeams bool) (*PrivilegePreviewResult, error) {
	groupName = strings.TrimSpace(groupName)
	if groupName == "" {
		return nil, errors.New("group_name is required")
	}
	if atTime <= 0 {
		atTime = time.Now().Unix()
	}

	var teams []models.Team
	if err := db.DB.Where("is_disabled = ?", false).
		Order("display_name ASC, name ASC, id ASC").Find(&teams).Error; err != nil {
		return nil, err
	}
	teams = filterTeamsByGroup(teams, groupName)

	filtered := make([]models.Team, 0, len(teams))
	ids := make([]uint, 0, len(teams))
	for _, cat := range teams {
		if excludeTestTeams && isLikelyTestTeam(cat) {
			continue
		}
		filtered = append(filtered, cat)
		ids = append(ids, cat.ID)
	}

	if len(ids) == 0 {
		return &PrivilegePreviewResult{GroupName: groupName, GeneratedAt: atTime, Batches: []PrivilegePreviewBatch{}}, nil
	}

	type batchAccumulator struct {
		oncallers map[string]struct{}
		rows      []PrivilegePreviewRow
	}
	batches := map[int64]*batchAccumulator{}

	for _, cat := range filtered {
		var current struct {
			TeamID     uint
			EngineerID uint
			StartTime  int64
			EndTime    int64
			Overlay    int
			Name       string
			Email      string
		}
		err := db.DB.Table("shift_intervals AS si").
			Select("si.team_id, si.engineer_id, si.start_time, si.end_time, si.overlay, engineers.name, engineers.email").
			Joins("LEFT JOIN engineers ON engineers.id = si.engineer_id").
			Where("si.team_id = ? AND si.is_backup = ? AND si.start_time <= ? AND si.end_time >= ?", cat.ID, false, atTime, atTime).
			Order("si.overlay DESC, si.id DESC").
			Take(&current).Error
		if err != nil {
			continue
		}
		engineerEmail := normalizeEmail(current.Email)
		if engineerEmail == "" || current.EndTime <= 0 {
			continue
		}
		displayName := strings.TrimSpace(cat.DisplayName)
		if displayName == "" {
			displayName = cat.Name
		}
		acc, ok := batches[current.EndTime]
		if !ok {
			acc = &batchAccumulator{oncallers: map[string]struct{}{}, rows: make([]PrivilegePreviewRow, 0)}
			batches[current.EndTime] = acc
		}
		acc.oncallers[engineerEmail] = struct{}{}
		acc.rows = append(acc.rows, PrivilegePreviewRow{
			TeamID:          cat.ID,
			TeamDisplayName: displayName,
			TeamKey:         cat.Name,
			EngineerName:    strings.TrimSpace(current.Name),
			EngineerEmail:   engineerEmail,
			ExpiredAt:       current.EndTime,
		})
	}

	expiredList := make([]int64, 0, len(batches))
	for expiredAt := range batches {
		expiredList = append(expiredList, expiredAt)
	}
	sort.Slice(expiredList, func(i, j int) bool { return expiredList[i] < expiredList[j] })

	result := &PrivilegePreviewResult{
		GroupName:   groupName,
		GeneratedAt: atTime,
		Batches:     make([]PrivilegePreviewBatch, 0, len(expiredList)),
	}

	for _, expiredAt := range expiredList {
		acc := batches[expiredAt]
		oncallers := make([]string, 0, len(acc.oncallers))
		for email := range acc.oncallers {
			oncallers = append(oncallers, email)
		}
		sort.Strings(oncallers)
		sort.Slice(acc.rows, func(i, j int) bool {
			if acc.rows[i].TeamDisplayName != acc.rows[j].TeamDisplayName {
				return acc.rows[i].TeamDisplayName < acc.rows[j].TeamDisplayName
			}
			return acc.rows[i].EngineerEmail < acc.rows[j].EngineerEmail
		})
		result.Batches = append(result.Batches, PrivilegePreviewBatch{
			Request: PrivilageRequest{
				Name:      groupName,
				ExpiredAt: expiredAt,
				Oncallers: oncallers,
			},
			Rows: acc.rows,
		})
	}

	return result, nil
}

func (s *ScheduleService) SendPrivilege(groupName string, atTime int64, excludeTestTeams bool) (*PrivilegeSendSummary, error) {
	preview, err := s.BuildPrivilegePreview(groupName, atTime, excludeTestTeams)
	if err != nil {
		return nil, err
	}

	privURL := getPrivilegeURL()
	if privURL == "" {
		return nil, errors.New("ONCALL_PRIVILEGE_URL or DEVOPS_URL is not configured")
	}
	apiKey := getPrivilegeAPIKey()
	if apiKey == "" {
		return nil, errors.New("ONCALL_PRIVILEGE_API_KEY or DEVOPS_API_KEY is not configured")
	}

	client := &http.Client{Timeout: 10 * time.Second}
	results := make([]PrivilegeSendResult, 0, len(preview.Batches))
	for _, batch := range preview.Batches {
		bodyBytes, err := json.Marshal(batch.Request)
		if err != nil {
			return nil, err
		}
		req, err := http.NewRequest(http.MethodPost, privURL, bytes.NewBuffer(bodyBytes))
		if err != nil {
			return nil, err
		}
		req.Header.Set("Content-Type", "application/json")
		req.Header.Set("X-API-KEY", apiKey)

		resp, err := client.Do(req)
		if err != nil {
			return nil, err
		}
		respBody, readErr := io.ReadAll(resp.Body)
		resp.Body.Close()
		if readErr != nil {
			return nil, readErr
		}
		result := PrivilegeSendResult{Request: batch.Request, StatusCode: resp.StatusCode, ResponseBody: string(respBody)}
		results = append(results, result)
		if resp.StatusCode >= 400 {
			return &PrivilegeSendSummary{Preview: preview, Results: results}, fmt.Errorf("privilege request failed: code=%d body=%s", resp.StatusCode, string(respBody))
		}
	}

	return &PrivilegeSendSummary{Preview: preview, Results: results}, nil
}
