package main

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"sync"

	"github.com/pquerna/otp/totp"
	"golang.org/x/crypto/bcrypt"
)

type Config struct {
	// Application
	AppName      string `json:"app_name"`

	// Authentication
	Username           string `json:"username"`
	HashedPass         string `json:"hashed_password"`
	MFASecret          string `json:"mfa_secret"`
	SessionTimeoutMins int    `json:"session_timeout_minutes"`

	// Server
	Port     string `json:"port"`
	BindAddr string `json:"bind_address"`

	// Blocklist settings
	UpdateDelay   int `json:"update_delay_days"`
	CheckInterval int `json:"check_interval_seconds"`

	// File paths
	PidFile        string `json:"pid_file"`
	ProcessName    string `json:"process_name"`
	TmpFile        string `json:"tmp_file"`
	LastUpdateFile string `json:"last_update_file"`
	URLFileList    string `json:"url_file_list"`
	BlocklistFile  string `json:"blocklist_file"`
	RemoveFile     string `json:"remove_file"`
	MergedListTmp  string `json:"merged_list_tmp"`

	// Default blocklist URLs
	DefaultURLs []string `json:"default_urls"`
}

var (
	config     Config
	configFile = "config.json"
	configMu   sync.RWMutex
)

func getDefaultConfig() Config {
	return Config{
		AppName:            "Unifi Blocklist Manager",
		Username:           "admin",
		HashedPass:         "", // Will be set with hashed password
		MFASecret:          "",
		SessionTimeoutMins: 60, // 1 hour default
		Port:               "8080",
		BindAddr:           "0.0.0.0",
		UpdateDelay:    3,
		CheckInterval:  5,
		PidFile:        "/tmp/coredns_last.pid",
		ProcessName:    "coredns",
		TmpFile:        "/sdcard1/combined-blocklist.txt",
		LastUpdateFile: "/sdcard1/last_update.txt",
		URLFileList:    "/sdcard1/urllist.txt",
		BlocklistFile:  "/run/utm/domain_list/domainlist_0.list",
		RemoveFile:     "/run/utm/domain_list/domainlist_1.list",
		MergedListTmp:  "/tmp/mergedlist.txt",
		DefaultURLs: []string{
			"https://adguardteam.github.io/HostlistsRegistry/assets/filter_27.txt",
			"https://adguardteam.github.io/HostlistsRegistry/assets/filter_49.txt",
			"https://adguardteam.github.io/HostlistsRegistry/assets/filter_1.txt",
			"https://adguardteam.github.io/HostlistsRegistry/assets/filter_42.txt",
			"https://adguardteam.github.io/HostlistsRegistry/assets/filter_18.txt",
			"https://adguardteam.github.io/HostlistsRegistry/assets/filter_23.txt",
			"https://adguardteam.github.io/HostlistsRegistry/assets/filter_11.txt",
			"https://adguardteam.github.io/HostlistsRegistry/assets/filter_9.txt",
			"https://adguardteam.github.io/HostlistsRegistry/assets/filter_50.txt",
			"https://raw.githubusercontent.com/DandelionSprout/adfilt/master/Alternate%20versions%20Anti-Malware%20List/AntiMalwareHosts.txt",
			"https://osint.digitalside.it/Threat-Intel/lists/latestdomains.txt",
			"https://v.firebog.net/hosts/Prigent-Crypto.txt",
			"https://phishing.army/download/phishing_army_blocklist_extended.txt",
			"https://v.firebog.net/hosts/static/w3kbl.txt",
		},
	}
}

func loadConfig() {
	configMu.Lock()
	defer configMu.Unlock()

	file, err := os.Open(configFile)
	if err != nil {
		log.Println("Config not found, creating default")
		// Create default config with admin user
		defaultConfig := getDefaultConfig()
		hashed, _ := bcrypt.GenerateFromPassword([]byte("Password123!"), bcrypt.DefaultCost)
		defaultConfig.HashedPass = string(hashed)
		config = defaultConfig
		saveConfigLocked()
		log.Println("Default config created with admin/Password123!")
		return
	}
	defer file.Close()

	err = json.NewDecoder(file).Decode(&config)
	if err != nil {
		log.Printf("Error reading config: %v, using defaults", err)
		defaultConfig := getDefaultConfig()
		hashed, _ := bcrypt.GenerateFromPassword([]byte("Password123!"), bcrypt.DefaultCost)
		defaultConfig.HashedPass = string(hashed)
		config = defaultConfig
	}
}

func saveConfigLocked() {
	file, err := os.Create(configFile)
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	encoder := json.NewEncoder(file)
	encoder.SetIndent("", "  ")
	err = encoder.Encode(config)
	if err != nil {
		log.Fatal(err)
	}
}

func saveConfig() {
	configMu.Lock()
	defer configMu.Unlock()

	saveConfigLocked()
}

func handleMFACommand(cmd string) {
	switch cmd {
	case "on":
		configMu.Lock()
		if config.MFASecret == "" {
			// Generate new secret
			key, err := totp.Generate(totp.GenerateOpts{
				Issuer:      "UnifiBlocklist",
				AccountName: config.Username,
			})
			if err != nil {
				fmt.Fprintf(os.Stderr, "Error generating MFA: %v\n", err)
				configMu.Unlock()
				os.Exit(1)
			}
			config.MFASecret = key.Secret()
			saveConfigLocked()
			fmt.Printf("MFA enabled.\nSecret: %s\nOTP URL: %s\n", key.Secret(), key.URL())
		} else {
			fmt.Println("MFA is already enabled")
			configMu.Unlock()
			return
		}
		configMu.Unlock()

	case "off":
		configMu.Lock()
		if config.MFASecret != "" {
			config.MFASecret = ""
			saveConfigLocked()
			fmt.Println("MFA disabled")
		} else {
			fmt.Println("MFA is not enabled")
		}
		configMu.Unlock()

	case "reset":
		configMu.Lock()
		key, err := totp.Generate(totp.GenerateOpts{
			Issuer:      "UnifiBlocklist",
			AccountName: config.Username,
		})
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error generating MFA: %v\n", err)
			configMu.Unlock()
			os.Exit(1)
		}
		config.MFASecret = key.Secret()
		saveConfigLocked()
		fmt.Printf("MFA reset.\nNew Secret: %s\nOTP URL: %s\nScan with your authenticator app.\n", key.Secret(), key.URL())
		configMu.Unlock()

	default:
		fmt.Fprintf(os.Stderr, "Invalid MFA command. Use: on, off, or reset\n")
		os.Exit(1)
	}
}
