package services

import (
	"context"
	"fmt"
	"time"

	"github.com/google/uuid"
	"github.com/jmoiron/sqlx"
)

type ReportingService struct {
	db *sqlx.DB
}

func NewReportingService(db *sqlx.DB) *ReportingService {
	return &ReportingService{db: db}
}

// --- Case Statistics ---

type CaseStats struct {
	Period     string `json:"period" db:"period"`
	Opened     int    `json:"opened" db:"opened"`
	Closed     int    `json:"closed" db:"closed"`
	Active     int    `json:"active" db:"active"`
}

type CasesByType struct {
	CaseType string `json:"case_type" db:"case_type"`
	Count    int    `json:"count" db:"count"`
}

type CasesByCourt struct {
	Court string `json:"court" db:"court"`
	Count int    `json:"count" db:"count"`
}

type CaseReport struct {
	Monthly  []CaseStats    `json:"monthly"`
	ByType   []CasesByType  `json:"by_type"`
	ByCourt  []CasesByCourt `json:"by_court"`
	Total    CaseReportTotals `json:"total"`
}

type CaseReportTotals struct {
	Opened int `json:"opened"`
	Closed int `json:"closed"`
	Active int `json:"active"`
}

func (s *ReportingService) CaseReport(ctx context.Context, tenantID uuid.UUID, from, to time.Time) (*CaseReport, error) {
	report := &CaseReport{}

	// Monthly breakdown
	monthlyQuery := `
		SELECT
			TO_CHAR(DATE_TRUNC('month', created_at), 'YYYY-MM') AS period,
			COUNT(*) AS opened,
			COUNT(*) FILTER (WHERE status IN ('closed', 'archived')) AS closed,
			COUNT(*) FILTER (WHERE status = 'active') AS active
		FROM cases
		WHERE tenant_id = $1 AND created_at >= $2 AND created_at <= $3
		GROUP BY DATE_TRUNC('month', created_at)
		ORDER BY DATE_TRUNC('month', created_at)`

	report.Monthly = []CaseStats{}
	if err := s.db.SelectContext(ctx, &report.Monthly, monthlyQuery, tenantID, from, to); err != nil {
		return nil, fmt.Errorf("case report monthly: %w", err)
	}

	// By type
	typeQuery := `
		SELECT COALESCE(case_type, 'Sonstiges') AS case_type, COUNT(*) AS count
		FROM cases
		WHERE tenant_id = $1 AND created_at >= $2 AND created_at <= $3
		GROUP BY case_type
		ORDER BY count DESC`

	report.ByType = []CasesByType{}
	if err := s.db.SelectContext(ctx, &report.ByType, typeQuery, tenantID, from, to); err != nil {
		return nil, fmt.Errorf("case report by type: %w", err)
	}

	// By court
	courtQuery := `
		SELECT COALESCE(court, 'Ohne Gericht') AS court, COUNT(*) AS count
		FROM cases
		WHERE tenant_id = $1 AND created_at >= $2 AND created_at <= $3
		GROUP BY court
		ORDER BY count DESC`

	report.ByCourt = []CasesByCourt{}
	if err := s.db.SelectContext(ctx, &report.ByCourt, courtQuery, tenantID, from, to); err != nil {
		return nil, fmt.Errorf("case report by court: %w", err)
	}

	// Totals
	totalsQuery := `
		SELECT
			COUNT(*) AS opened,
			COUNT(*) FILTER (WHERE status IN ('closed', 'archived')) AS closed,
			COUNT(*) FILTER (WHERE status = 'active') AS active
		FROM cases
		WHERE tenant_id = $1 AND created_at >= $2 AND created_at <= $3`

	if err := s.db.GetContext(ctx, &report.Total, totalsQuery, tenantID, from, to); err != nil {
		return nil, fmt.Errorf("case report totals: %w", err)
	}

	return report, nil
}

// --- Deadline Compliance ---

type DeadlineCompliance struct {
	Period       string  `json:"period" db:"period"`
	Total        int     `json:"total" db:"total"`
	Met          int     `json:"met" db:"met"`
	Missed       int     `json:"missed" db:"missed"`
	Pending      int     `json:"pending" db:"pending"`
	ComplianceRate float64 `json:"compliance_rate"`
}

type MissedDeadline struct {
	ID         uuid.UUID `json:"id" db:"id"`
	Title      string    `json:"title" db:"title"`
	DueDate    string    `json:"due_date" db:"due_date"`
	CaseID     uuid.UUID `json:"case_id" db:"case_id"`
	CaseNumber string    `json:"case_number" db:"case_number"`
	CaseTitle  string    `json:"case_title" db:"case_title"`
	DaysOverdue int      `json:"days_overdue" db:"days_overdue"`
}

type DeadlineReport struct {
	Monthly []DeadlineCompliance `json:"monthly"`
	Missed  []MissedDeadline     `json:"missed"`
	Total   DeadlineReportTotals `json:"total"`
}

type DeadlineReportTotals struct {
	Total          int     `json:"total"`
	Met            int     `json:"met"`
	Missed         int     `json:"missed"`
	Pending        int     `json:"pending"`
	ComplianceRate float64 `json:"compliance_rate"`
}

func (s *ReportingService) DeadlineReport(ctx context.Context, tenantID uuid.UUID, from, to time.Time) (*DeadlineReport, error) {
	report := &DeadlineReport{}

	// Monthly compliance
	monthlyQuery := `
		SELECT
			TO_CHAR(DATE_TRUNC('month', due_date), 'YYYY-MM') AS period,
			COUNT(*) AS total,
			COUNT(*) FILTER (WHERE status = 'completed' AND (completed_at IS NULL OR completed_at::date <= due_date)) AS met,
			COUNT(*) FILTER (WHERE (status = 'completed' AND completed_at::date > due_date) OR (status = 'pending' AND due_date < CURRENT_DATE)) AS missed,
			COUNT(*) FILTER (WHERE status = 'pending' AND due_date >= CURRENT_DATE) AS pending
		FROM deadlines
		WHERE tenant_id = $1 AND due_date >= $2 AND due_date <= $3
		GROUP BY DATE_TRUNC('month', due_date)
		ORDER BY DATE_TRUNC('month', due_date)`

	report.Monthly = []DeadlineCompliance{}
	if err := s.db.SelectContext(ctx, &report.Monthly, monthlyQuery, tenantID, from, to); err != nil {
		return nil, fmt.Errorf("deadline report monthly: %w", err)
	}

	// Calculate compliance rates
	for i := range report.Monthly {
		completed := report.Monthly[i].Met + report.Monthly[i].Missed
		if completed > 0 {
			report.Monthly[i].ComplianceRate = float64(report.Monthly[i].Met) / float64(completed) * 100
		}
	}

	// Missed deadlines list
	missedQuery := `
		SELECT d.id, d.title, d.due_date, d.case_id, c.case_number, c.title AS case_title,
			(CURRENT_DATE - d.due_date::date) AS days_overdue
		FROM deadlines d
		JOIN cases c ON c.id = d.case_id AND c.tenant_id = d.tenant_id
		WHERE d.tenant_id = $1 AND d.due_date >= $2 AND d.due_date <= $3
			AND ((d.status = 'pending' AND d.due_date < CURRENT_DATE)
				OR (d.status = 'completed' AND d.completed_at::date > d.due_date))
		ORDER BY d.due_date ASC
		LIMIT 50`

	report.Missed = []MissedDeadline{}
	if err := s.db.SelectContext(ctx, &report.Missed, missedQuery, tenantID, from, to); err != nil {
		return nil, fmt.Errorf("deadline report missed: %w", err)
	}

	// Totals
	totalsQuery := `
		SELECT
			COUNT(*) AS total,
			COUNT(*) FILTER (WHERE status = 'completed' AND (completed_at IS NULL OR completed_at::date <= due_date)) AS met,
			COUNT(*) FILTER (WHERE (status = 'completed' AND completed_at::date > due_date) OR (status = 'pending' AND due_date < CURRENT_DATE)) AS missed,
			COUNT(*) FILTER (WHERE status = 'pending' AND due_date >= CURRENT_DATE) AS pending
		FROM deadlines
		WHERE tenant_id = $1 AND due_date >= $2 AND due_date <= $3`

	if err := s.db.GetContext(ctx, &report.Total, totalsQuery, tenantID, from, to); err != nil {
		return nil, fmt.Errorf("deadline report totals: %w", err)
	}

	completed := report.Total.Met + report.Total.Missed
	if completed > 0 {
		report.Total.ComplianceRate = float64(report.Total.Met) / float64(completed) * 100
	}

	return report, nil
}

// --- Workload ---

type UserWorkload struct {
	UserID      uuid.UUID `json:"user_id" db:"user_id"`
	ActiveCases int       `json:"active_cases" db:"active_cases"`
	Deadlines   int       `json:"deadlines" db:"deadlines"`
	Overdue     int       `json:"overdue" db:"overdue"`
	Completed   int       `json:"completed" db:"completed"`
}

type WorkloadReport struct {
	Users []UserWorkload `json:"users"`
}

func (s *ReportingService) WorkloadReport(ctx context.Context, tenantID uuid.UUID, from, to time.Time) (*WorkloadReport, error) {
	report := &WorkloadReport{}

	query := `
		WITH user_cases AS (
			SELECT ca.user_id, COUNT(DISTINCT ca.case_id) AS active_cases
			FROM case_assignments ca
			JOIN cases c ON c.id = ca.case_id AND c.tenant_id = $1
			WHERE c.status = 'active'
			GROUP BY ca.user_id
		),
		user_deadlines AS (
			SELECT ca.user_id,
				COUNT(*) AS deadlines,
				COUNT(*) FILTER (WHERE d.status = 'pending' AND d.due_date < CURRENT_DATE) AS overdue,
				COUNT(*) FILTER (WHERE d.status = 'completed' AND d.completed_at >= $2 AND d.completed_at <= $3) AS completed
			FROM case_assignments ca
			JOIN deadlines d ON d.case_id = ca.case_id AND d.tenant_id = $1
			WHERE d.due_date >= $2 AND d.due_date <= $3
			GROUP BY ca.user_id
		)
		SELECT
			COALESCE(uc.user_id, ud.user_id) AS user_id,
			COALESCE(uc.active_cases, 0) AS active_cases,
			COALESCE(ud.deadlines, 0) AS deadlines,
			COALESCE(ud.overdue, 0) AS overdue,
			COALESCE(ud.completed, 0) AS completed
		FROM user_cases uc
		FULL OUTER JOIN user_deadlines ud ON uc.user_id = ud.user_id
		ORDER BY active_cases DESC`

	report.Users = []UserWorkload{}
	if err := s.db.SelectContext(ctx, &report.Users, query, tenantID, from, to); err != nil {
		return nil, fmt.Errorf("workload report: %w", err)
	}

	return report, nil
}

// --- Billing (summary from case data) ---

type BillingByMonth struct {
	Period      string `json:"period" db:"period"`
	CasesActive int    `json:"cases_active" db:"cases_active"`
	CasesClosed int    `json:"cases_closed" db:"cases_closed"`
	CasesNew    int    `json:"cases_new" db:"cases_new"`
}

type BillingByType struct {
	CaseType string `json:"case_type" db:"case_type"`
	Active   int    `json:"active" db:"active"`
	Closed   int    `json:"closed" db:"closed"`
	Total    int    `json:"total" db:"total"`
}

type BillingReport struct {
	Monthly []BillingByMonth `json:"monthly"`
	ByType  []BillingByType  `json:"by_type"`
}

func (s *ReportingService) BillingReport(ctx context.Context, tenantID uuid.UUID, from, to time.Time) (*BillingReport, error) {
	report := &BillingReport{}

	// Monthly activity for billing overview
	monthlyQuery := `
		SELECT
			TO_CHAR(DATE_TRUNC('month', created_at), 'YYYY-MM') AS period,
			COUNT(*) FILTER (WHERE status = 'active') AS cases_active,
			COUNT(*) FILTER (WHERE status IN ('closed', 'archived')) AS cases_closed,
			COUNT(*) AS cases_new
		FROM cases
		WHERE tenant_id = $1 AND created_at >= $2 AND created_at <= $3
		GROUP BY DATE_TRUNC('month', created_at)
		ORDER BY DATE_TRUNC('month', created_at)`

	report.Monthly = []BillingByMonth{}
	if err := s.db.SelectContext(ctx, &report.Monthly, monthlyQuery, tenantID, from, to); err != nil {
		return nil, fmt.Errorf("billing report monthly: %w", err)
	}

	// By case type
	typeQuery := `
		SELECT
			COALESCE(case_type, 'Sonstiges') AS case_type,
			COUNT(*) FILTER (WHERE status = 'active') AS active,
			COUNT(*) FILTER (WHERE status IN ('closed', 'archived')) AS closed,
			COUNT(*) AS total
		FROM cases
		WHERE tenant_id = $1 AND created_at >= $2 AND created_at <= $3
		GROUP BY case_type
		ORDER BY total DESC`

	report.ByType = []BillingByType{}
	if err := s.db.SelectContext(ctx, &report.ByType, typeQuery, tenantID, from, to); err != nil {
		return nil, fmt.Errorf("billing report by type: %w", err)
	}

	return report, nil
}
