package payments

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

	"github.com/google/uuid"
)

// railAvailability reads a rail's operational availability. A missing row is fail-open: enabled with
// no message, so payments stay on unless an operator explicitly disabled the rail.
func (s *Store) railAvailability(ctx context.Context, rail string) (RailAvailability, error) {
	var a RailAvailability
	err := s.db.QueryRowContext(ctx,
		`SELECT enabled, message_ru, message_en FROM payments.rail_status WHERE rail = $1`, rail).
		Scan(&a.Enabled, &a.MessageRU, &a.MessageEN)
	if errors.Is(err, sql.ErrNoRows) {
		return RailAvailability{Enabled: true}, nil
	}
	if err != nil {
		return RailAvailability{}, fmt.Errorf("payments: read rail status %q: %w", rail, err)
	}
	return a, nil
}

// allRailStatus reads every stored rail-status row for the admin editor, keyed by rail. Rails with
// no row are absent (the caller fills them with the fail-open enabled default).
func (s *Store) allRailStatus(ctx context.Context) (map[string]RailAvailability, error) {
	rows, err := s.db.QueryContext(ctx,
		`SELECT rail, enabled, message_ru, message_en FROM payments.rail_status`)
	if err != nil {
		return nil, fmt.Errorf("payments: read rail statuses: %w", err)
	}
	defer rows.Close()
	out := make(map[string]RailAvailability)
	for rows.Next() {
		var rail string
		var a RailAvailability
		if err := rows.Scan(&rail, &a.Enabled, &a.MessageRU, &a.MessageEN); err != nil {
			return nil, fmt.Errorf("payments: scan rail status: %w", err)
		}
		out[rail] = a
	}
	return out, rows.Err()
}

// setRailStatus upserts a rail's operational status (admin).
func (s *Store) setRailStatus(ctx context.Context, rail string, a RailAvailability, now time.Time) error {
	if _, err := s.db.ExecContext(ctx,
		`INSERT INTO payments.rail_status (rail, enabled, message_ru, message_en, updated_at)
		 VALUES ($1, $2, $3, $4, $5)
		 ON CONFLICT (rail) DO UPDATE SET enabled = $2, message_ru = $3, message_en = $4, updated_at = $5`,
		rail, a.Enabled, a.MessageRU, a.MessageEN, now); err != nil {
		return fmt.Errorf("payments: set rail status %q: %w", rail, err)
	}
	return nil
}

// purchaseOverride reads an account's purchase override; a missing row is OverrideDefault.
func (s *Store) purchaseOverride(ctx context.Context, accountID uuid.UUID) (PurchaseOverride, error) {
	var allow bool
	err := s.db.QueryRowContext(ctx,
		`SELECT allow FROM payments.account_payment_override WHERE account_id = $1`, accountID).Scan(&allow)
	if errors.Is(err, sql.ErrNoRows) {
		return OverrideDefault, nil
	}
	if err != nil {
		return OverrideDefault, fmt.Errorf("payments: read purchase override %s: %w", accountID, err)
	}
	if allow {
		return OverrideAllow, nil
	}
	return OverrideDeny, nil
}

// setPurchaseOverride upserts an account's override, or deletes the row for OverrideDefault (the
// default state is the absence of a row).
func (s *Store) setPurchaseOverride(ctx context.Context, accountID uuid.UUID, ov PurchaseOverride, now time.Time) error {
	if ov == OverrideDefault {
		if _, err := s.db.ExecContext(ctx,
			`DELETE FROM payments.account_payment_override WHERE account_id = $1`, accountID); err != nil {
			return fmt.Errorf("payments: clear purchase override %s: %w", accountID, err)
		}
		return nil
	}
	if _, err := s.db.ExecContext(ctx,
		`INSERT INTO payments.account_payment_override (account_id, allow, updated_at) VALUES ($1, $2, $3)
		 ON CONFLICT (account_id) DO UPDATE SET allow = $2, updated_at = $3`,
		accountID, ov == OverrideAllow, now); err != nil {
		return fmt.Errorf("payments: set purchase override %s: %w", accountID, err)
	}
	return nil
}
