package main

import (
	"database/sql"
	"fmt"
	"strconv"
	"strings"
)

const (
	settingViewProofInView       = "view_proof_in_view"
	settingLiveActiveView        = "live_active_view"
	settingLiveShowGroupLive     = "live_show_group_live"
	settingLiveGroupDisplayMode  = "live_group_display_mode"
	settingLiveGroupFixedCode    = "live_group_fixed_code"
	settingLiveShowPrelimTie     = "live_show_prelim_tie"
	settingLiveShowPrelimOverall = "live_show_prelim_overall"
	settingLiveShowFinalGroups   = "live_show_final_groups"
	settingLiveFinalDisplayMode  = "live_final_display_mode"
	settingLiveFinalFixedGroup   = "live_final_fixed_group"
	settingLiveShowFinalTie      = "live_show_final_tie"
	settingLiveShowPodium        = "live_show_podium"
	settingLiveRotationInterval  = "live_rotation_interval_sec"
	settingLiveRotationPlayers   = "live_rotation_player_count"
)

func defaultLiveModeSettings() LiveModeSettings {
	return LiveModeSettings{
		ActiveView:          "group_live",
		ShowGroupLive:       true,
		GroupDisplayMode:    "rotate",
		GroupFixedCode:      "",
		ShowPrelimTie:       true,
		ShowPrelimOverall:   true,
		ShowFinalGroups:     true,
		FinalDisplayMode:    "rotate",
		FinalFixedGroup:     1,
		ShowFinalTie:        true,
		ShowPodium:          true,
		RotationIntervalSec: 5,
		RotationPlayerCount: 12,
	}
}

func (a *App) readSettings() (AppSettings, error) {
	live := defaultLiveModeSettings()

	viewProofRaw, err := a.readSettingValue(settingViewProofInView)
	if err != nil {
		return AppSettings{}, fmt.Errorf("read app setting %s: %w", settingViewProofInView, err)
	}

	if raw, err := a.readSettingValue(settingLiveShowGroupLive); err == nil {
		live.ShowGroupLive = settingBool(raw)
	} else {
		return AppSettings{}, fmt.Errorf("read app setting %s: %w", settingLiveShowGroupLive, err)
	}
	if raw, err := a.readSettingValue(settingLiveActiveView); err == nil {
		live.ActiveView = normalizeLiveActiveView(raw, live.ActiveView)
	} else {
		return AppSettings{}, fmt.Errorf("read app setting %s: %w", settingLiveActiveView, err)
	}

	if raw, err := a.readSettingValue(settingLiveGroupDisplayMode); err == nil {
		live.GroupDisplayMode = normalizeLiveDisplayMode(raw, live.GroupDisplayMode)
	} else {
		return AppSettings{}, fmt.Errorf("read app setting %s: %w", settingLiveGroupDisplayMode, err)
	}

	if raw, err := a.readSettingValue(settingLiveGroupFixedCode); err == nil {
		live.GroupFixedCode = strings.ToUpper(strings.TrimSpace(raw))
	} else {
		return AppSettings{}, fmt.Errorf("read app setting %s: %w", settingLiveGroupFixedCode, err)
	}

	if raw, err := a.readSettingValue(settingLiveShowPrelimTie); err == nil {
		live.ShowPrelimTie = settingBool(raw)
	} else {
		return AppSettings{}, fmt.Errorf("read app setting %s: %w", settingLiveShowPrelimTie, err)
	}

	if raw, err := a.readSettingValue(settingLiveShowPrelimOverall); err == nil {
		live.ShowPrelimOverall = settingBool(raw)
	} else {
		return AppSettings{}, fmt.Errorf("read app setting %s: %w", settingLiveShowPrelimOverall, err)
	}

	if raw, err := a.readSettingValue(settingLiveShowFinalGroups); err == nil {
		live.ShowFinalGroups = settingBool(raw)
	} else {
		return AppSettings{}, fmt.Errorf("read app setting %s: %w", settingLiveShowFinalGroups, err)
	}

	if raw, err := a.readSettingValue(settingLiveFinalDisplayMode); err == nil {
		live.FinalDisplayMode = normalizeLiveDisplayMode(raw, live.FinalDisplayMode)
	} else {
		return AppSettings{}, fmt.Errorf("read app setting %s: %w", settingLiveFinalDisplayMode, err)
	}

	if raw, err := a.readSettingValue(settingLiveFinalFixedGroup); err == nil {
		live.FinalFixedGroup = normalizeLiveFinalFixedGroup(raw, live.FinalFixedGroup)
	} else {
		return AppSettings{}, fmt.Errorf("read app setting %s: %w", settingLiveFinalFixedGroup, err)
	}

	if raw, err := a.readSettingValue(settingLiveShowFinalTie); err == nil {
		live.ShowFinalTie = settingBool(raw)
	} else {
		return AppSettings{}, fmt.Errorf("read app setting %s: %w", settingLiveShowFinalTie, err)
	}

	if raw, err := a.readSettingValue(settingLiveShowPodium); err == nil {
		live.ShowPodium = settingBool(raw)
	} else {
		return AppSettings{}, fmt.Errorf("read app setting %s: %w", settingLiveShowPodium, err)
	}

	if raw, err := a.readSettingValue(settingLiveRotationInterval); err == nil {
		live.RotationIntervalSec = normalizeRotationInterval(raw, live.RotationIntervalSec)
	} else {
		return AppSettings{}, fmt.Errorf("read app setting %s: %w", settingLiveRotationInterval, err)
	}
	if raw, err := a.readSettingValue(settingLiveRotationPlayers); err == nil {
		live.RotationPlayerCount = normalizeRotationPlayerCount(raw, live.RotationPlayerCount)
	} else {
		return AppSettings{}, fmt.Errorf("read app setting %s: %w", settingLiveRotationPlayers, err)
	}

	return AppSettings{
		ViewProofInView: settingBool(viewProofRaw),
		LiveMode:        live,
	}, nil
}

func (a *App) updateSettings(req AdminSettingsUpdateRequest) error {
	tx, err := a.db.Begin()
	if err != nil {
		return fmt.Errorf("begin settings transaction: %w", err)
	}
	defer tx.Rollback()

	hasUpdate := false

	if req.ViewProofInView != nil {
		hasUpdate = true
		if err := upsertSetting(tx, settingViewProofInView, settingString(*req.ViewProofInView)); err != nil {
			return err
		}
	}

	if req.LiveMode != nil {
		patch := req.LiveMode
		if patch.ActiveView != nil {
			hasUpdate = true
			value := normalizeLiveActiveView(*patch.ActiveView, "group_live")
			if err := upsertSetting(tx, settingLiveActiveView, value); err != nil {
				return err
			}
		}
		if patch.ShowGroupLive != nil {
			hasUpdate = true
			if err := upsertSetting(tx, settingLiveShowGroupLive, settingString(*patch.ShowGroupLive)); err != nil {
				return err
			}
		}
		if patch.GroupDisplayMode != nil {
			hasUpdate = true
			value := normalizeLiveDisplayMode(*patch.GroupDisplayMode, "rotate")
			if err := upsertSetting(tx, settingLiveGroupDisplayMode, value); err != nil {
				return err
			}
		}
		if patch.GroupFixedCode != nil {
			hasUpdate = true
			value := strings.ToUpper(strings.TrimSpace(*patch.GroupFixedCode))
			if err := upsertSetting(tx, settingLiveGroupFixedCode, value); err != nil {
				return err
			}
		}
		if patch.ShowPrelimTie != nil {
			hasUpdate = true
			if err := upsertSetting(tx, settingLiveShowPrelimTie, settingString(*patch.ShowPrelimTie)); err != nil {
				return err
			}
		}
		if patch.ShowPrelimOverall != nil {
			hasUpdate = true
			if err := upsertSetting(tx, settingLiveShowPrelimOverall, settingString(*patch.ShowPrelimOverall)); err != nil {
				return err
			}
		}
		if patch.ShowFinalGroups != nil {
			hasUpdate = true
			if err := upsertSetting(tx, settingLiveShowFinalGroups, settingString(*patch.ShowFinalGroups)); err != nil {
				return err
			}
		}
		if patch.FinalDisplayMode != nil {
			hasUpdate = true
			value := normalizeLiveDisplayMode(*patch.FinalDisplayMode, "rotate")
			if err := upsertSetting(tx, settingLiveFinalDisplayMode, value); err != nil {
				return err
			}
		}
		if patch.FinalFixedGroup != nil {
			hasUpdate = true
			value := normalizeLiveFinalFixedGroupInt(*patch.FinalFixedGroup)
			if err := upsertSetting(tx, settingLiveFinalFixedGroup, strconv.Itoa(value)); err != nil {
				return err
			}
		}
		if patch.ShowFinalTie != nil {
			hasUpdate = true
			if err := upsertSetting(tx, settingLiveShowFinalTie, settingString(*patch.ShowFinalTie)); err != nil {
				return err
			}
		}
		if patch.ShowPodium != nil {
			hasUpdate = true
			if err := upsertSetting(tx, settingLiveShowPodium, settingString(*patch.ShowPodium)); err != nil {
				return err
			}
		}
		if patch.RotationIntervalSec != nil {
			hasUpdate = true
			value := normalizeRotationIntervalInt(*patch.RotationIntervalSec)
			if err := upsertSetting(tx, settingLiveRotationInterval, strconv.Itoa(value)); err != nil {
				return err
			}
		}
		if patch.RotationPlayerCount != nil {
			hasUpdate = true
			value := normalizeRotationPlayerCountInt(*patch.RotationPlayerCount)
			if err := upsertSetting(tx, settingLiveRotationPlayers, strconv.Itoa(value)); err != nil {
				return err
			}
		}
	}

	if !hasUpdate {
		return fmt.Errorf("no settings to update")
	}

	if err := tx.Commit(); err != nil {
		return fmt.Errorf("commit settings transaction: %w", err)
	}

	return nil
}

func upsertSetting(tx *sql.Tx, key string, value string) error {
	_, err := tx.Exec(`
INSERT INTO app_settings(key, value, updated_at)
VALUES(?, ?, CURRENT_TIMESTAMP)
ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = CURRENT_TIMESTAMP
`, key, value)
	if err != nil {
		return fmt.Errorf("update app setting %s: %w", key, err)
	}
	return nil
}

func (a *App) readSettingValue(key string) (string, error) {
	var raw string
	err := a.db.QueryRow(`SELECT value FROM app_settings WHERE key = ?`, key).Scan(&raw)
	if err != nil {
		if err == sql.ErrNoRows {
			return "", nil
		}
		return "", err
	}
	return raw, nil
}

func settingBool(value string) bool {
	switch strings.TrimSpace(strings.ToLower(value)) {
	case "1", "true", "yes", "on":
		return true
	default:
		return false
	}
}

func settingString(value bool) string {
	if value {
		return "1"
	}
	return "0"
}

func normalizeLiveDisplayMode(value string, fallback string) string {
	normalized := strings.TrimSpace(strings.ToLower(value))
	if normalized == "fixed" {
		return "fixed"
	}
	if normalized == "rotate" {
		return "rotate"
	}
	if strings.TrimSpace(strings.ToLower(fallback)) == "fixed" {
		return "fixed"
	}
	return "rotate"
}

func normalizeLiveFinalFixedGroup(value string, fallback int) int {
	parsed, err := strconv.Atoi(strings.TrimSpace(value))
	if err != nil {
		return normalizeLiveFinalFixedGroupInt(fallback)
	}
	return normalizeLiveFinalFixedGroupInt(parsed)
}

func normalizeLiveFinalFixedGroupInt(value int) int {
	if value == 2 {
		return 2
	}
	return 1
}

func normalizeRotationInterval(raw string, fallback int) int {
	parsed, err := strconv.Atoi(strings.TrimSpace(raw))
	if err != nil {
		return normalizeRotationIntervalInt(fallback)
	}
	return normalizeRotationIntervalInt(parsed)
}

func normalizeRotationIntervalInt(value int) int {
	if value < 3 {
		return 3
	}
	if value > 30 {
		return 30
	}
	return value
}

func normalizeRotationPlayerCount(raw string, fallback int) int {
	parsed, err := strconv.Atoi(strings.TrimSpace(raw))
	if err != nil {
		return normalizeRotationPlayerCountInt(fallback)
	}
	return normalizeRotationPlayerCountInt(parsed)
}

func normalizeRotationPlayerCountInt(value int) int {
	if value < 3 {
		return 3
	}
	if value > 40 {
		return 40
	}
	return value
}

func normalizeLiveActiveView(value string, fallback string) string {
	switch strings.TrimSpace(strings.ToLower(value)) {
	case "group_live", "prelim_tie", "prelim_overall", "final_groups", "final_tie", "podium":
		return strings.TrimSpace(strings.ToLower(value))
	default:
		if strings.TrimSpace(strings.ToLower(fallback)) != "" {
			return strings.TrimSpace(strings.ToLower(fallback))
		}
		return "group_live"
	}
}
