package main

import (
	"io"
	"log"
	"net/http"
	"os"
	"os/exec"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"time"
)

func updateBlocklist(force int) {
	configMu.RLock()
	delayDays := config.UpdateDelay
	configMu.RUnlock()

	if checkIfUpdateNeeded(delayDays, force) {
		createDefaultURLList()
		fetchAndMergeSources()
		applyRemovalRules()
		finalizeBlocklist()
	} else {
		handleSkippedUpdate(delayDays)
	}
}

func checkIfUpdateNeeded(delayDays, force int) bool {
	configMu.RLock()
	lastUpdateFile := config.LastUpdateFile
	configMu.RUnlock()

	data, err := os.ReadFile(lastUpdateFile)
	if err != nil {
		return true
	}
	lastTS, _ := strconv.ParseInt(strings.TrimSpace(string(data)), 10, 64)
	nowTS := time.Now().Unix()

	delaySeconds := int64(delayDays * 86400)

	if force == 0 && nowTS-lastTS < delaySeconds {
		return false
	}
	return true
}

func handleSkippedUpdate(delayDays int) {
	configMu.RLock()
	blocklistFile := config.BlocklistFile
	tmpFile := config.TmpFile
	configMu.RUnlock()

	log.Printf("%s: Full Update skipped — last update was less than %d days ago. Use force to override.", time.Now(), delayDays)

	blockData, _ := os.ReadFile(blocklistFile)
	tmpData, _ := os.ReadFile(tmpFile)

	if string(blockData) == string(tmpData) {
		log.Printf("%s: Custom Blocklist still in use, doing nothing.", time.Now())
		return
	}

	time.Sleep(10 * time.Second)
	log.Printf("%s: Copying existing blocklist to %s", time.Now(), blocklistFile)

	merged := mergeUnique(string(blockData), string(tmpData))
	os.WriteFile(blocklistFile, []byte(merged), 0644)

	log.Printf("%s: Restarting CoreDNS to apply new blocklist file.", time.Now())
	exec.Command("pkill", "coredns").Run()
}

func createDefaultURLList() {
	configMu.RLock()
	urlFileList := config.URLFileList
	defaultURLs := config.DefaultURLs
	configMu.RUnlock()

	_, err := os.Stat(urlFileList)
	if os.IsNotExist(err) {
		log.Printf("%s: Using Default url list: %s", time.Now(), urlFileList)
		writeLines(urlFileList, defaultURLs)
	}
}

func fetchAndMergeSources() {
	configMu.RLock()
	tmpFile := config.TmpFile
	blocklistFile := config.BlocklistFile
	urlFileList := config.URLFileList
	configMu.RUnlock()

	os.Create(tmpFile)

	blockData, _ := os.ReadFile(blocklistFile)
	var builder strings.Builder
	builder.Write(blockData)

	urls, _ := readLines(urlFileList)
	for _, url := range urls {
		if strings.HasPrefix(url, "#") {
			continue
		}
		resp, err := http.Get(url)
		if err != nil {
			continue
		}
		body, _ := io.ReadAll(resp.Body)
		resp.Body.Close()

		lines := strings.Split(string(body), "\n")
		for _, line := range lines {
			line = strings.TrimSpace(line)
			if line == "" || strings.HasPrefix(line, "!") || strings.HasPrefix(line, "#") {
				continue
			}
			line = strings.TrimPrefix(line, "||")
			line = strings.TrimSuffix(line, "^")
			builder.WriteString(line + "\n")
		}
	}

	lines := strings.Split(builder.String(), "\n")
	sort.Strings(lines)
	unique := []string{}
	seen := make(map[string]bool)
	for _, line := range lines {
		line = strings.TrimSpace(line)
		if line == "" || seen[line] {
			continue
		}
		if !isValidDomain(line) {
			continue
		}
		unique = append(unique, line)
		seen[line] = true
	}

	os.WriteFile(tmpFile, []byte(strings.Join(unique, "\n")), 0644)
	log.Printf("%s: Combined List cleanup.", time.Now())
}

func applyRemovalRules() {
	configMu.RLock()
	removeFile := config.RemoveFile
	tmpFile := config.TmpFile
	configMu.RUnlock()

	removeLines, _ := readLines(removeFile)
	var patterns []*regexp.Regexp
	for _, rule := range removeLines {
		rule = strings.TrimSpace(rule)
		if rule == "" {
			continue
		}
		rule = regexp.QuoteMeta(rule)
		rule = strings.ReplaceAll(rule, "\\*", ".*")
		re, _ := regexp.Compile("^" + rule + "$")
		patterns = append(patterns, re)
	}

	data, _ := os.ReadFile(tmpFile)
	lines := strings.Split(string(data), "\n")
	var filtered []string
	for _, line := range lines {
		drop := false
		for _, re := range patterns {
			if re.MatchString(line) {
				drop = true
				break
			}
		}
		if !drop {
			filtered = append(filtered, line)
		}
	}

	os.WriteFile(tmpFile+".filtered", []byte(strings.Join(filtered, "\n")), 0644)
}

func finalizeBlocklist() {
	configMu.RLock()
	tmpFile := config.TmpFile
	blocklistFile := config.BlocklistFile
	lastUpdateFile := config.LastUpdateFile
	configMu.RUnlock()

	os.Rename(tmpFile+".filtered", tmpFile)
	copyFile(tmpFile, blocklistFile)

	now := time.Now().Unix()
	os.WriteFile(lastUpdateFile, []byte(strconv.FormatInt(now, 10)), 0644)

	log.Printf("%s: Restarting CoreDNS to apply new blocklist file", time.Now())
	exec.Command("pkill", "coredns").Run()

	stat, _ := os.Stat(blocklistFile)
	size := stat.Size()
	lines, _ := readLines(blocklistFile)
	log.Printf("%s: Blocklist created at: %s with size %d and %d lines", time.Now(), blocklistFile, size, len(lines))
}
