package db

import (
	"context"
	"fmt"
	"time"

	"github.com/jackc/pgx/v5"
)

// ============================================
// SMS SETTINGS - PG REPOSITORY
// ============================================

func (r *PGRepository) GetTenantSMSSettings(ctx context.Context, tenantID string) (TenantSMSSettingsRecord, error) {
	var rec TenantSMSSettingsRecord
	err := r.pool.QueryRow(ctx, `
		SELECT tenant_id, enabled, COALESCE(sender_name, ''), COALESCE(monthly_limit, 0), COALESCE(stripe_subscription_item_id, '')
		FROM tenant_sms_settings
		WHERE tenant_id = $1
	`, tenantID).Scan(&rec.TenantID, &rec.Enabled, &rec.SenderName, &rec.MonthlyLimit, &rec.StripeSubscriptionItemID)
	if err != nil {
		if err == pgx.ErrNoRows {
			return TenantSMSSettingsRecord{TenantID: tenantID}, nil
		}
		return TenantSMSSettingsRecord{}, err
	}
	return rec, nil
}

func (r *PGRepository) UpsertTenantSMSSettings(ctx context.Context, params TenantSMSSettingsRecord) error {
	_, err := r.pool.Exec(ctx, `
		INSERT INTO tenant_sms_settings (tenant_id, enabled, sender_name, monthly_limit, stripe_subscription_item_id, updated_at)
		VALUES ($1, $2, $3, $4, $5, now())
		ON CONFLICT (tenant_id) DO UPDATE SET
			enabled = EXCLUDED.enabled,
			sender_name = EXCLUDED.sender_name,
			monthly_limit = EXCLUDED.monthly_limit,
			stripe_subscription_item_id = EXCLUDED.stripe_subscription_item_id,
			updated_at = now()
	`, params.TenantID, params.Enabled, params.SenderName, params.MonthlyLimit, params.StripeSubscriptionItemID)
	return err
}

// ============================================
// SMS USAGE LOGS - PG REPOSITORY
// ============================================

func (r *PGRepository) CreateSMSUsageLog(ctx context.Context, params SMSUsageLogRecord) (string, error) {
	var id string
	err := r.pool.QueryRow(ctx, `
		INSERT INTO sms_usage_logs (tenant_id, recipient_phone, message_body, external_message_id, external_request_id, status, cost_cents, sent_at)
		VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
		RETURNING id
	`, params.TenantID, params.RecipientPhone, params.MessageBody, params.ExternalMessageID, params.ExternalRequestID, params.Status, params.CostCents, params.SentAt).Scan(&id)
	return id, err
}

func (r *PGRepository) GetSMSUsageThisMonth(ctx context.Context, tenantID string) (SMSUsageSummary, error) {
	var summary SMSUsageSummary
	err := r.pool.QueryRow(ctx, `
		SELECT COALESCE(COUNT(*), 0), COALESCE(SUM(cost_cents), 0)
		FROM sms_usage_logs
		WHERE tenant_id = $1 AND date_trunc('month', created_at) = date_trunc('month', now())
	`, tenantID).Scan(&summary.MessageCount, &summary.TotalCostCents)
	return summary, err
}

func (r *PGRepository) GetSMSUsageForMonth(ctx context.Context, tenantID string, yearMonth string) (SMSMonthlyReportRecord, error) {
	var rec SMSMonthlyReportRecord
	err := r.pool.QueryRow(ctx, `
		SELECT COALESCE(COUNT(*), 0), COALESCE(SUM(cost_cents), 0)
		FROM sms_usage_logs
		WHERE tenant_id = $1 AND to_char(created_at, 'YYYY-MM') = $2
	`, tenantID, yearMonth).Scan(&rec.MessageCount, &rec.TotalCostCents)
	if err != nil {
		return SMSMonthlyReportRecord{}, err
	}
	rec.TenantID = tenantID
	rec.YearMonth = yearMonth
	return rec, nil
}

func (r *PGRepository) ListSMSUsageLogs(ctx context.Context, tenantID string, limit int) ([]SMSUsageLogRecord, error) {
	rows, err := r.pool.Query(ctx, `
		SELECT id, tenant_id, recipient_phone, message_body, external_message_id, external_request_id, status, cost_cents, created_at
		FROM sms_usage_logs
		WHERE tenant_id = $1
		ORDER BY created_at DESC
		LIMIT $2
	`, tenantID, limit)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var records []SMSUsageLogRecord
	for rows.Next() {
		var rec SMSUsageLogRecord
		if err := rows.Scan(&rec.ID, &rec.TenantID, &rec.RecipientPhone, &rec.MessageBody, &rec.ExternalMessageID, &rec.ExternalRequestID, &rec.Status, &rec.CostCents, &rec.CreatedAt); err != nil {
			return nil, err
		}
		records = append(records, rec)
	}
	return records, rows.Err()
}

func (r *PGRepository) ListSMSMonthlyReports(ctx context.Context, tenantID string, limit int) ([]SMSMonthlyReportRecord, error) {
	rows, err := r.pool.Query(ctx, `
		SELECT id, tenant_id, year_month, message_count, total_cost_cents, stripe_invoice_id, invoice_sent_at, created_at
		FROM sms_monthly_reports
		WHERE tenant_id = $1
		ORDER BY year_month DESC
		LIMIT $2
	`, tenantID, limit)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var records []SMSMonthlyReportRecord
	for rows.Next() {
		var rec SMSMonthlyReportRecord
		if err := rows.Scan(&rec.ID, &rec.TenantID, &rec.YearMonth, &rec.MessageCount, &rec.TotalCostCents, &rec.StripeInvoiceID, &rec.InvoiceSentAt, &rec.CreatedAt); err != nil {
			return nil, err
		}
		records = append(records, rec)
	}
	return records, rows.Err()
}

func (r *PGRepository) UpsertSMSMonthlyReport(ctx context.Context, params SMSMonthlyReportRecord) error {
	_, err := r.pool.Exec(ctx, `
		INSERT INTO sms_monthly_reports (tenant_id, year_month, message_count, total_cost_cents, stripe_invoice_id)
		VALUES ($1, $2, $3, $4, $5)
		ON CONFLICT (tenant_id, year_month) DO UPDATE SET
			message_count = EXCLUDED.message_count,
			total_cost_cents = EXCLUDED.total_cost_cents,
			stripe_invoice_id = EXCLUDED.stripe_invoice_id,
			created_at = now()
	`, params.TenantID, params.YearMonth, params.MessageCount, params.TotalCostCents, params.StripeInvoiceID)
	return err
}

func (r *PGRepository) MarkSMSReportInvoiceSent(ctx context.Context, tenantID string, yearMonth string) error {
	_, err := r.pool.Exec(ctx, `
		UPDATE sms_monthly_reports
		SET invoice_sent_at = now()
		WHERE tenant_id = $1 AND year_month = $2
	`, tenantID, yearMonth)
	return err
}

func (r *PGRepository) ListTenantsWithSMSUsage(ctx context.Context, yearMonth string) ([]TenantRecord, error) {
	rows, err := r.pool.Query(ctx, `
		SELECT DISTINCT t.id, t.slug, t.name, t.preset, t.locale, t.timezone, t.plan_code, t.subscription_status,
		       t.billing_provider, t.billing_customer_id, t.billing_subscription_id
		FROM tenants t
		JOIN tenant_sms_settings s ON s.tenant_id = t.id AND s.enabled = true
		JOIN sms_usage_logs l ON l.tenant_id = t.id AND to_char(l.created_at, 'YYYY-MM') = $1
	`, yearMonth)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var records []TenantRecord
	for rows.Next() {
		var rec TenantRecord
		if err := rows.Scan(&rec.ID, &rec.Slug, &rec.Name, &rec.Preset, &rec.Locale, &rec.Timezone, &rec.PlanCode, &rec.SubscriptionStatus,
			&rec.BillingProvider, &rec.BillingCustomerID, &rec.BillingSubscription); err != nil {
			return nil, err
		}
		records = append(records, rec)
	}
	return records, rows.Err()
}

// ============================================
// SMS SETTINGS - MEMORY REPOSITORY
// ============================================

func (r *MemoryRepository) GetTenantSMSSettings(_ context.Context, tenantID string) (TenantSMSSettingsRecord, error) {
	if tenantID != r.tenant.ID {
		return TenantSMSSettingsRecord{}, pgx.ErrNoRows
	}
	return r.smsSettings, nil
}

func (r *MemoryRepository) UpsertTenantSMSSettings(_ context.Context, params TenantSMSSettingsRecord) error {
	r.smsSettings = params
	return nil
}

func (r *MemoryRepository) CreateSMSUsageLog(_ context.Context, params SMSUsageLogRecord) (string, error) {
	params.ID = fmt.Sprintf("sms-%d", len(r.smsLogs))
	params.CreatedAt = time.Now().UTC()
	r.smsLogs = append([]SMSUsageLogRecord{params}, r.smsLogs...)
	return params.ID, nil
}

func (r *MemoryRepository) GetSMSUsageThisMonth(_ context.Context, tenantID string) (SMSUsageSummary, error) {
	if tenantID != r.tenant.ID {
		return SMSUsageSummary{}, nil
	}
	now := time.Now().UTC()
	var count, cost int
	for _, log := range r.smsLogs {
		if log.TenantID == tenantID && log.CreatedAt.Year() == now.Year() && log.CreatedAt.Month() == now.Month() {
			count++
			cost += log.CostCents
		}
	}
	return SMSUsageSummary{MessageCount: count, TotalCostCents: cost}, nil
}

func (r *MemoryRepository) GetSMSUsageForMonth(_ context.Context, tenantID string, yearMonth string) (SMSMonthlyReportRecord, error) {
	if tenantID != r.tenant.ID {
		return SMSMonthlyReportRecord{}, nil
	}
	var count, cost int
	for _, log := range r.smsLogs {
		if log.TenantID == tenantID && log.CreatedAt.Format("2006-01") == yearMonth {
			count++
			cost += log.CostCents
		}
	}
	return SMSMonthlyReportRecord{TenantID: tenantID, YearMonth: yearMonth, MessageCount: count, TotalCostCents: cost}, nil
}

func (r *MemoryRepository) ListSMSUsageLogs(_ context.Context, tenantID string, limit int) ([]SMSUsageLogRecord, error) {
	if tenantID != r.tenant.ID {
		return nil, nil
	}
	if limit > len(r.smsLogs) {
		limit = len(r.smsLogs)
	}
	return r.smsLogs[:limit], nil
}

func (r *MemoryRepository) ListSMSMonthlyReports(_ context.Context, tenantID string, limit int) ([]SMSMonthlyReportRecord, error) {
	if tenantID != r.tenant.ID {
		return nil, nil
	}
	if limit > len(r.smsReports) {
		limit = len(r.smsReports)
	}
	return r.smsReports[:limit], nil
}

func (r *MemoryRepository) UpsertSMSMonthlyReport(_ context.Context, params SMSMonthlyReportRecord) error {
	for i, rep := range r.smsReports {
		if rep.TenantID == params.TenantID && rep.YearMonth == params.YearMonth {
			r.smsReports[i] = params
			return nil
		}
	}
	r.smsReports = append([]SMSMonthlyReportRecord{params}, r.smsReports...)
	return nil
}

func (r *MemoryRepository) MarkSMSReportInvoiceSent(_ context.Context, tenantID string, yearMonth string) error {
	now := time.Now().UTC()
	for i, rep := range r.smsReports {
		if rep.TenantID == tenantID && rep.YearMonth == yearMonth {
			r.smsReports[i].InvoiceSentAt = &now
			return nil
		}
	}
	return nil
}

func (r *MemoryRepository) ListTenantsWithSMSUsage(_ context.Context, yearMonth string) ([]TenantRecord, error) {
	for _, log := range r.smsLogs {
		if log.TenantID == r.tenant.ID && log.CreatedAt.Format("2006-01") == yearMonth && r.smsSettings.Enabled {
			return []TenantRecord{r.tenant}, nil
		}
	}
	return nil, nil
}
