package services

import (
	"context"
	"database/sql"
	"fmt"
	"time"

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

	"mgit.msbls.de/m/KanzlAI-mGMT/internal/models"
)

// DeadlineService handles CRUD operations for case deadlines
type DeadlineService struct {
	db    *sqlx.DB
	audit *AuditService
}

// NewDeadlineService creates a new deadline service
func NewDeadlineService(db *sqlx.DB, audit *AuditService) *DeadlineService {
	return &DeadlineService{db: db, audit: audit}
}

// ListAll returns all deadlines for a tenant, ordered by due_date
func (s *DeadlineService) ListAll(tenantID uuid.UUID) ([]models.Deadline, error) {
	query := `SELECT id, tenant_id, case_id, title, description, due_date, original_due_date,
	                 warning_date, source, rule_id, status, completed_at,
	                 caldav_uid, caldav_etag, notes, created_at, updated_at
	          FROM deadlines
	          WHERE tenant_id = $1
	          ORDER BY due_date ASC`

	var deadlines []models.Deadline
	err := s.db.Select(&deadlines, query, tenantID)
	if err != nil {
		return nil, fmt.Errorf("listing all deadlines: %w", err)
	}
	return deadlines, nil
}

// ListForCase returns all deadlines for a case, scoped to tenant
func (s *DeadlineService) ListForCase(tenantID, caseID uuid.UUID) ([]models.Deadline, error) {
	query := `SELECT id, tenant_id, case_id, title, description, due_date, original_due_date,
	                 warning_date, source, rule_id, status, completed_at,
	                 caldav_uid, caldav_etag, notes, created_at, updated_at
	          FROM deadlines
	          WHERE tenant_id = $1 AND case_id = $2
	          ORDER BY due_date ASC`

	var deadlines []models.Deadline
	err := s.db.Select(&deadlines, query, tenantID, caseID)
	if err != nil {
		return nil, fmt.Errorf("listing deadlines for case: %w", err)
	}
	return deadlines, nil
}

// GetByID returns a single deadline by ID, scoped to tenant
func (s *DeadlineService) GetByID(tenantID, deadlineID uuid.UUID) (*models.Deadline, error) {
	query := `SELECT id, tenant_id, case_id, title, description, due_date, original_due_date,
	                 warning_date, source, rule_id, status, completed_at,
	                 caldav_uid, caldav_etag, notes, created_at, updated_at
	          FROM deadlines
	          WHERE tenant_id = $1 AND id = $2`

	var d models.Deadline
	err := s.db.Get(&d, query, tenantID, deadlineID)
	if err != nil {
		if err == sql.ErrNoRows {
			return nil, nil
		}
		return nil, fmt.Errorf("getting deadline: %w", err)
	}
	return &d, nil
}

// CreateDeadlineInput holds the fields for creating a deadline
type CreateDeadlineInput struct {
	CaseID      uuid.UUID  `json:"case_id"`
	Title       string     `json:"title"`
	Description *string    `json:"description,omitempty"`
	DueDate     string     `json:"due_date"`
	WarningDate *string    `json:"warning_date,omitempty"`
	Source      string     `json:"source"`
	RuleID      *uuid.UUID `json:"rule_id,omitempty"`
	Notes       *string    `json:"notes,omitempty"`
}

// Create inserts a new deadline
func (s *DeadlineService) Create(ctx context.Context, tenantID uuid.UUID, input CreateDeadlineInput) (*models.Deadline, error) {
	id := uuid.New()
	source := input.Source
	if source == "" {
		source = "manual"
	}

	query := `INSERT INTO deadlines (id, tenant_id, case_id, title, description, due_date,
	                                  warning_date, source, rule_id, status, notes,
	                                  created_at, updated_at)
	          VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, 'pending', $10, NOW(), NOW())
	          RETURNING id, tenant_id, case_id, title, description, due_date, original_due_date,
	                    warning_date, source, rule_id, status, completed_at,
	                    caldav_uid, caldav_etag, notes, created_at, updated_at`

	var d models.Deadline
	err := s.db.Get(&d, query, id, tenantID, input.CaseID, input.Title, input.Description,
		input.DueDate, input.WarningDate, source, input.RuleID, input.Notes)
	if err != nil {
		return nil, fmt.Errorf("creating deadline: %w", err)
	}
	s.audit.Log(ctx, "create", "deadline", &id, nil, d)
	return &d, nil
}

// UpdateDeadlineInput holds the fields for updating a deadline
type UpdateDeadlineInput struct {
	Title       *string    `json:"title,omitempty"`
	Description *string    `json:"description,omitempty"`
	DueDate     *string    `json:"due_date,omitempty"`
	WarningDate *string    `json:"warning_date,omitempty"`
	Notes       *string    `json:"notes,omitempty"`
	Status      *string    `json:"status,omitempty"`
	RuleID      *uuid.UUID `json:"rule_id,omitempty"`
}

// Update modifies an existing deadline
func (s *DeadlineService) Update(ctx context.Context, tenantID, deadlineID uuid.UUID, input UpdateDeadlineInput) (*models.Deadline, error) {
	// First check it exists and belongs to tenant
	existing, err := s.GetByID(tenantID, deadlineID)
	if err != nil {
		return nil, err
	}
	if existing == nil {
		return nil, nil
	}

	query := `UPDATE deadlines SET
	            title = COALESCE($1, title),
	            description = COALESCE($2, description),
	            due_date = COALESCE($3, due_date),
	            warning_date = COALESCE($4, warning_date),
	            notes = COALESCE($5, notes),
	            status = COALESCE($6, status),
	            rule_id = COALESCE($7, rule_id),
	            updated_at = NOW()
	          WHERE id = $8 AND tenant_id = $9
	          RETURNING id, tenant_id, case_id, title, description, due_date, original_due_date,
	                    warning_date, source, rule_id, status, completed_at,
	                    caldav_uid, caldav_etag, notes, created_at, updated_at`

	var d models.Deadline
	err = s.db.Get(&d, query, input.Title, input.Description, input.DueDate,
		input.WarningDate, input.Notes, input.Status, input.RuleID,
		deadlineID, tenantID)
	if err != nil {
		return nil, fmt.Errorf("updating deadline: %w", err)
	}
	s.audit.Log(ctx, "update", "deadline", &deadlineID, existing, d)
	return &d, nil
}

// Complete marks a deadline as completed
func (s *DeadlineService) Complete(ctx context.Context, tenantID, deadlineID uuid.UUID) (*models.Deadline, error) {
	query := `UPDATE deadlines SET
	            status = 'completed',
	            completed_at = $1,
	            updated_at = NOW()
	          WHERE id = $2 AND tenant_id = $3
	          RETURNING id, tenant_id, case_id, title, description, due_date, original_due_date,
	                    warning_date, source, rule_id, status, completed_at,
	                    caldav_uid, caldav_etag, notes, created_at, updated_at`

	var d models.Deadline
	err := s.db.Get(&d, query, time.Now(), deadlineID, tenantID)
	if err != nil {
		if err == sql.ErrNoRows {
			return nil, nil
		}
		return nil, fmt.Errorf("completing deadline: %w", err)
	}
	s.audit.Log(ctx, "update", "deadline", &deadlineID, map[string]string{"status": "pending"}, map[string]string{"status": "completed"})
	return &d, nil
}

// Delete removes a deadline
func (s *DeadlineService) Delete(ctx context.Context, tenantID, deadlineID uuid.UUID) error {
	query := `DELETE FROM deadlines WHERE id = $1 AND tenant_id = $2`
	result, err := s.db.Exec(query, deadlineID, tenantID)
	if err != nil {
		return fmt.Errorf("deleting deadline: %w", err)
	}
	rows, err := result.RowsAffected()
	if err != nil {
		return fmt.Errorf("checking delete result: %w", err)
	}
	if rows == 0 {
		return fmt.Errorf("deadline not found")
	}
	s.audit.Log(ctx, "delete", "deadline", &deadlineID, nil, nil)
	return nil
}
