package api

import (
	"context"
	"database/sql"
	"encoding/json"
	"fmt"
	"strings"
	"time"
)

const (
	kanbanSchemaWorkpulseReportV1 = "workpulse.report.v1"
	calendarKindReportSummary     = "report_summary"
)

type kanbanBodyMinimal struct {
	Schema          string `json:"schema"`
	WorkDescription string `json:"workDescription"`
	Blockers        string `json:"blockers"`
	Tomorrow        string `json:"tomorrow"`
	Tasks           []struct {
		Title string `json:"title"`
	} `json:"tasks"`
}

func parseReportDateUTC(raw string) (time.Time, bool) {
	raw = strings.TrimSpace(raw)
	if raw == "" {
		return time.Time{}, false
	}
	head := raw
	if len(head) > 10 {
		head = head[:10]
	}
	d, err := time.ParseInLocation("2006-01-02", head, time.UTC)
	if err != nil {
		return time.Time{}, false
	}
	return d, true
}

// reportBodyCalendarEligible returns task count and whether the report should appear on the calendar.
// Eligible when report_date is set and body has Kanban tasks, workDescription, or legacy blockers/tomorrow text.
func reportBodyCalendarEligible(body string) (taskCount int, eligible bool) {
	body = strings.TrimSpace(body)
	if body == "" {
		return 0, false
	}
	if !strings.HasPrefix(body, "{") {
		return 0, true
	}
	var kb kanbanBodyMinimal
	if err := json.Unmarshal([]byte(body), &kb); err != nil {
		return 0, false
	}
	if strings.TrimSpace(kb.Schema) != kanbanSchemaWorkpulseReportV1 {
		return 0, false
	}
	for _, t := range kb.Tasks {
		if strings.TrimSpace(t.Title) != "" {
			taskCount++
		}
	}
	if taskCount > 0 {
		return taskCount, true
	}
	if strings.TrimSpace(kb.WorkDescription) != "" {
		return 0, true
	}
	if strings.TrimSpace(kb.Blockers) != "" || strings.TrimSpace(kb.Tomorrow) != "" {
		return 0, true
	}
	if reportAttachmentCount(body) > 0 {
		return 0, true
	}
	return 0, false
}

func calendarDisplayTitle(reportTitle, body string, taskCount int) string {
	displayTitle := strings.TrimSpace(reportTitle)
	if displayTitle != "" {
		if len(displayTitle) > 500 {
			return displayTitle[:500]
		}
		return displayTitle
	}
	if strings.HasPrefix(strings.TrimSpace(body), "{") {
		var kb kanbanBodyMinimal
		if json.Unmarshal([]byte(body), &kb) == nil {
			if desc := strings.TrimSpace(kb.WorkDescription); desc != "" {
				if len(desc) > 120 {
					desc = desc[:120] + "…"
				}
				return desc
			}
			if desc := strings.TrimSpace(kb.Blockers); desc != "" {
				if len(desc) > 120 {
					desc = desc[:120] + "…"
				}
				return desc
			}
		}
	}
	if taskCount > 0 {
		return fmt.Sprintf("Laporan harian · %d tugas", taskCount)
	}
	return "Laporan harian"
}

// syncReportCalendarEvent keeps at most one calendar row per report (kind=report_summary) when report_date is set
// and body has meaningful content. Draft and submitted both sync. Times are UTC day bounds for report_date.
func (s *Server) syncReportCalendarEvent(ctx context.Context, reportID uint64) error {
	var uid uint64
	var teamID sql.NullInt64
	var title, body string
	var reportDate sql.NullString

	err := s.DB.QueryRowContext(ctx,
		`SELECT user_id, team_id, title, body, report_date FROM reports WHERE id = $1`,
		reportID,
	).Scan(&uid, &teamID, &title, &body, &reportDate)
	if err == sql.ErrNoRows {
		return nil
	}
	if err != nil {
		return err
	}

	if _, err := s.DB.ExecContext(ctx,
		`DELETE FROM calendar_events WHERE report_id = $1 AND kind = $2`,
		reportID, calendarKindReportSummary,
	); err != nil {
		return err
	}

	if !reportDate.Valid || strings.TrimSpace(reportDate.String) == "" {
		return nil
	}
	taskCount, eligible := reportBodyCalendarEligible(body)
	if !eligible {
		return nil
	}

	d, ok := parseReportDateUTC(reportDate.String)
	if !ok {
		return nil
	}
	start := time.Date(d.Year(), d.Month(), d.Day(), 0, 0, 0, 0, time.UTC)
	end := start.AddDate(0, 0, 1) // exclusive end (all-day on report_date only)

	displayTitle := calendarDisplayTitle(title, body, taskCount)

	var teamArg interface{}
	if teamID.Valid {
		teamArg = teamID.Int64
	} else {
		teamArg = nil
	}

	_, err = s.DB.ExecContext(ctx,
		`INSERT INTO calendar_events (user_id, team_id, title, starts_at, ends_at, all_day, report_id, kind)
		 VALUES ($1, $2, $3, $4, $5, true, $6, $7)`,
		uid, teamArg, displayTitle, start, end, reportID, calendarKindReportSummary,
	)
	return err
}

// BackfillReportCalendarEvents re-syncs calendar rows for all reports that have report_date set.
func (s *Server) BackfillReportCalendarEvents(ctx context.Context) (int, error) {
	rows, err := s.DB.QueryContext(ctx,
		`SELECT id FROM reports WHERE report_date IS NOT NULL AND TRIM(COALESCE(report_date::text, '')) <> '' ORDER BY id`,
	)
	if err != nil {
		return 0, err
	}
	defer rows.Close()
	n := 0
	for rows.Next() {
		var id uint64
		if err := rows.Scan(&id); err != nil {
			return n, err
		}
		if err := s.syncReportCalendarEvent(ctx, id); err != nil {
			return n, err
		}
		n++
	}
	return n, rows.Err()
}
