package services

import (
	"context"
	"fmt"

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

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

type BillingRateService struct {
	db    *sqlx.DB
	audit *AuditService
}

func NewBillingRateService(db *sqlx.DB, audit *AuditService) *BillingRateService {
	return &BillingRateService{db: db, audit: audit}
}

type UpsertBillingRateInput struct {
	UserID    *uuid.UUID `json:"user_id,omitempty"`
	Rate      float64    `json:"rate"`
	Currency  string     `json:"currency"`
	ValidFrom string     `json:"valid_from"`
	ValidTo   *string    `json:"valid_to,omitempty"`
}

func (s *BillingRateService) List(ctx context.Context, tenantID uuid.UUID) ([]models.BillingRate, error) {
	var rates []models.BillingRate
	err := s.db.SelectContext(ctx, &rates,
		`SELECT id, tenant_id, user_id, rate, currency, valid_from, valid_to, created_at
		 FROM billing_rates
		 WHERE tenant_id = $1
		 ORDER BY valid_from DESC, user_id NULLS LAST`,
		tenantID)
	if err != nil {
		return nil, fmt.Errorf("list billing rates: %w", err)
	}
	return rates, nil
}

func (s *BillingRateService) Upsert(ctx context.Context, tenantID uuid.UUID, input UpsertBillingRateInput) (*models.BillingRate, error) {
	if input.Currency == "" {
		input.Currency = "EUR"
	}

	// Close any existing open-ended rate for this user
	_, err := s.db.ExecContext(ctx,
		`UPDATE billing_rates SET valid_to = $3
		 WHERE tenant_id = $1
		 AND (($2::uuid IS NULL AND user_id IS NULL) OR user_id = $2)
		 AND valid_to IS NULL
		 AND valid_from < $3`,
		tenantID, input.UserID, input.ValidFrom)
	if err != nil {
		return nil, fmt.Errorf("close existing rate: %w", err)
	}

	var rate models.BillingRate
	err = s.db.QueryRowxContext(ctx,
		`INSERT INTO billing_rates (tenant_id, user_id, rate, currency, valid_from, valid_to)
		 VALUES ($1, $2, $3, $4, $5, $6)
		 RETURNING id, tenant_id, user_id, rate, currency, valid_from, valid_to, created_at`,
		tenantID, input.UserID, input.Rate, input.Currency, input.ValidFrom, input.ValidTo,
	).StructScan(&rate)
	if err != nil {
		return nil, fmt.Errorf("upsert billing rate: %w", err)
	}

	s.audit.Log(ctx, "create", "billing_rate", &rate.ID, nil, rate)
	return &rate, nil
}

func (s *BillingRateService) GetCurrentRate(ctx context.Context, tenantID uuid.UUID, userID uuid.UUID, date string) (*float64, error) {
	var rate float64
	err := s.db.GetContext(ctx, &rate,
		`SELECT rate FROM billing_rates
		 WHERE tenant_id = $1 AND (user_id = $2 OR user_id IS NULL)
		 AND valid_from <= $3 AND (valid_to IS NULL OR valid_to >= $3)
		 ORDER BY user_id NULLS LAST LIMIT 1`,
		tenantID, userID, date)
	if err != nil {
		return nil, err
	}
	return &rate, nil
}
