package sqlite

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

	"chat-switchboard/models"
	"chat-switchboard/store"
)

type HealthStore struct{}

func NewHealthStore() *HealthStore { return &HealthStore{} }

func (s *HealthStore) UpsertWindow(ctx context.Context, w *models.ProviderHealthWindow) error {
	id := store.NewID()
	windowStr := w.WindowStart.Format(timeFmt)
	_, err := DB.ExecContext(ctx, `
		INSERT INTO provider_health (id, provider_config_id, window_start,
			request_count, error_count, timeout_count, rate_limit_count,
			total_latency_ms, max_latency_ms, last_error, last_error_at, updated_at)
		VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'))
		ON CONFLICT (provider_config_id, window_start) DO UPDATE SET
			request_count    = provider_health.request_count + excluded.request_count,
			error_count      = provider_health.error_count + excluded.error_count,
			timeout_count    = provider_health.timeout_count + excluded.timeout_count,
			rate_limit_count = provider_health.rate_limit_count + excluded.rate_limit_count,
			total_latency_ms = provider_health.total_latency_ms + excluded.total_latency_ms,
			max_latency_ms   = MAX(provider_health.max_latency_ms, excluded.max_latency_ms),
			last_error       = COALESCE(excluded.last_error, provider_health.last_error),
			last_error_at    = COALESCE(excluded.last_error_at, provider_health.last_error_at),
			updated_at       = datetime('now')
	`, id, w.ProviderConfigID, windowStr,
		w.RequestCount, w.ErrorCount, w.TimeoutCount, w.RateLimitCount,
		w.TotalLatencyMs, w.MaxLatencyMs, w.LastError, w.LastErrorAt,
	)
	return err
}

func (s *HealthStore) GetCurrentWindow(ctx context.Context, providerConfigID string) (*models.ProviderHealthWindow, error) {
	windowStr := time.Now().UTC().Truncate(time.Hour).Format(timeFmt)
	var w models.ProviderHealthWindow
	var windowStartStr string
	err := DB.QueryRowContext(ctx, `
		SELECT id, provider_config_id, window_start,
			request_count, error_count, timeout_count, rate_limit_count,
			total_latency_ms, max_latency_ms, last_error, last_error_at
		FROM provider_health
		WHERE provider_config_id = ? AND window_start = ?
	`, providerConfigID, windowStr).Scan(
		&w.ID, &w.ProviderConfigID, &windowStartStr,
		&w.RequestCount, &w.ErrorCount, &w.TimeoutCount, &w.RateLimitCount,
		&w.TotalLatencyMs, &w.MaxLatencyMs, &w.LastError, &w.LastErrorAt,
	)
	if err == sql.ErrNoRows {
		return nil, nil
	}
	if err != nil {
		return nil, err
	}
	w.WindowStart, _ = time.Parse(timeFmt, windowStartStr)
	return &w, nil
}

func (s *HealthStore) ListWindows(ctx context.Context, providerConfigID string, hours int) ([]models.ProviderHealthWindow, error) {
	rows, err := DB.QueryContext(ctx, `
		SELECT id, provider_config_id, window_start,
			request_count, error_count, timeout_count, rate_limit_count,
			total_latency_ms, max_latency_ms, last_error, last_error_at
		FROM provider_health
		WHERE provider_config_id = ?
		ORDER BY window_start DESC
		LIMIT ?
	`, providerConfigID, hours)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	return scanHealthRows(rows)
}

func (s *HealthStore) ListAllCurrent(ctx context.Context) ([]models.ProviderHealthWindow, error) {
	windowStr := time.Now().UTC().Truncate(time.Hour).Format(timeFmt)
	rows, err := DB.QueryContext(ctx, `
		SELECT id, provider_config_id, window_start,
			request_count, error_count, timeout_count, rate_limit_count,
			total_latency_ms, max_latency_ms, last_error, last_error_at
		FROM provider_health
		WHERE window_start = ?
		ORDER BY provider_config_id
	`, windowStr)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	return scanHealthRows(rows)
}

func (s *HealthStore) Prune(ctx context.Context, before time.Time) (int64, error) {
	result, err := DB.ExecContext(ctx, `
		DELETE FROM provider_health WHERE window_start < ?
	`, before.Format(timeFmt))
	if err != nil {
		return 0, err
	}
	DB.ExecContext(ctx, `DELETE FROM tool_health WHERE window_start < ?`, before.Format(timeFmt))
	return result.RowsAffected()
}

// ── Tool Health (v0.22.4) ────────────────────

func (s *HealthStore) UpsertToolWindow(ctx context.Context, w *models.ToolHealthWindow) error {
	id := store.NewID()
	windowStr := w.WindowStart.Format(timeFmt)
	_, err := DB.ExecContext(ctx, `
		INSERT INTO tool_health (id, tool_name, window_start,
			request_count, error_count, total_latency_ms, max_latency_ms,
			last_error, last_error_at, updated_at)
		VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'))
		ON CONFLICT (tool_name, window_start) DO UPDATE SET
			request_count    = tool_health.request_count + excluded.request_count,
			error_count      = tool_health.error_count + excluded.error_count,
			total_latency_ms = tool_health.total_latency_ms + excluded.total_latency_ms,
			max_latency_ms   = MAX(tool_health.max_latency_ms, excluded.max_latency_ms),
			last_error       = COALESCE(excluded.last_error, tool_health.last_error),
			last_error_at    = COALESCE(excluded.last_error_at, tool_health.last_error_at),
			updated_at       = datetime('now')
	`, id, w.ToolName, windowStr,
		w.RequestCount, w.ErrorCount, w.TotalLatencyMs, w.MaxLatencyMs,
		w.LastError, w.LastErrorAt,
	)
	return err
}

func (s *HealthStore) ListAllToolCurrent(ctx context.Context) ([]models.ToolHealthWindow, error) {
	windowStr := time.Now().UTC().Truncate(time.Hour).Format(timeFmt)
	rows, err := DB.QueryContext(ctx, `
		SELECT id, tool_name, window_start,
			request_count, error_count, total_latency_ms, max_latency_ms,
			last_error, last_error_at
		FROM tool_health
		WHERE window_start = ?
		ORDER BY tool_name
	`, windowStr)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var result []models.ToolHealthWindow
	for rows.Next() {
		var w models.ToolHealthWindow
		var windowStartStr string
		if err := rows.Scan(
			&w.ID, &w.ToolName, &windowStartStr,
			&w.RequestCount, &w.ErrorCount, &w.TotalLatencyMs, &w.MaxLatencyMs,
			&w.LastError, &w.LastErrorAt,
		); err != nil {
			return nil, err
		}
		w.WindowStart, _ = time.Parse(timeFmt, windowStartStr)
		result = append(result, w)
	}
	return result, rows.Err()
}

func (s *HealthStore) DeactivateProvider(ctx context.Context, configID string) error {
	_, err := DB.ExecContext(ctx, `UPDATE provider_configs SET is_active = 0 WHERE id = ?`, configID)
	return err
}

func scanHealthRows(rows *sql.Rows) ([]models.ProviderHealthWindow, error) {
	var result []models.ProviderHealthWindow
	for rows.Next() {
		var w models.ProviderHealthWindow
		var windowStartStr string
		if err := rows.Scan(
			&w.ID, &w.ProviderConfigID, &windowStartStr,
			&w.RequestCount, &w.ErrorCount, &w.TimeoutCount, &w.RateLimitCount,
			&w.TotalLatencyMs, &w.MaxLatencyMs, &w.LastError, &w.LastErrorAt,
		); err != nil {
			return nil, err
		}
		w.WindowStart, _ = time.Parse(timeFmt, windowStartStr)
		result = append(result, w)
	}
	return result, rows.Err()
}
