package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"sort"

	"github.com/kv/clearnup/backend/internal/apps"
	"github.com/kv/clearnup/backend/internal/cleaner"
	"github.com/kv/clearnup/backend/internal/scanner"
)

const Port = ":36969"

func enableCors(w *http.ResponseWriter) {
	(*w).Header().Set("Access-Control-Allow-Origin", "*")
	(*w).Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
	(*w).Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
}

func main() {
	http.HandleFunc("/api/disk-usage", handleDiskUsage)
	http.HandleFunc("/api/scan/user", handleScanUser)
	http.HandleFunc("/api/scan/system", handleScanSystem) // Detailed list
	http.HandleFunc("/api/scan/sizes", handleScanSizes)   // Fast summary
	http.HandleFunc("/api/scan/deepest", handleDeepestScan)

	http.HandleFunc("/api/scan/category", handleScanCategory)
	http.HandleFunc("/api/purge", handlePurge)
	http.HandleFunc("/api/empty-trash", handleEmptyTrash)
	http.HandleFunc("/api/clear-cache", handleClearCache)
	http.HandleFunc("/api/clean-docker", handleCleanDocker)
	http.HandleFunc("/api/system-info", handleSystemInfo)
	http.HandleFunc("/api/estimates", handleCleaningEstimates)

	// App Uninstaller
	http.HandleFunc("/api/apps", handleScanApps)
	http.HandleFunc("/api/apps/details", handleAppDetails)
	http.HandleFunc("/api/apps/action", handleAppAction)

	fmt.Printf("🚀 Antigravity Backend running on http://localhost%s\n", Port)
	if err := http.ListenAndServe(Port, nil); err != nil {
		fmt.Printf("Server failed: %s\n", err)
	}
}

type ScanRequest struct {
	Category string `json:"category"` // "apps", "photos", "icloud", "docs", "system"
}

func handleScanCategory(w http.ResponseWriter, r *http.Request) {
	enableCors(&w)
	if r.Method == "OPTIONS" {
		return
	}

	var req ScanRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, "Invalid body", http.StatusBadRequest)
		return
	}

	home, _ := os.UserHomeDir()
	var targets []string

	switch req.Category {
	case "apps":
		targets = []string{"/Applications", filepath.Join(home, "Applications")}
	case "photos":
		targets = []string{filepath.Join(home, "Pictures")}
	case "icloud":
		targets = []string{filepath.Join(home, "Library", "Mobile Documents")}
	case "docs":
		targets = []string{filepath.Join(home, "Documents")}
	case "downloads":
		targets = []string{filepath.Join(home, "Downloads")}
	case "desktop":
		targets = []string{filepath.Join(home, "Desktop")}
	case "music":
		targets = []string{filepath.Join(home, "Music")}
	case "movies":
		targets = []string{filepath.Join(home, "Movies")}
	case "system":
		targets = []string{filepath.Join(home, "Library", "Caches"), filepath.Join(home, "Library", "Logs"), filepath.Join(home, "Library", "Developer", "Xcode", "DerivedData")}
	default:
		json.NewEncoder(w).Encode([]scanner.ScanResult{})
		return
	}

	// Reuse ScanPath logic inline or call a helper
	// We'll just do a quick loop here since ScanPath in scanner.go was defined but I need to link it
	// Actually I put ScanPath in scanner.go as FindLargeFiles wrapper.
	var allResults []scanner.ScanResult
	for _, t := range targets {
		res, _ := scanner.FindLargeFiles(t, 10*1024*1024) // 10MB
		allResults = append(allResults, res...)
	}

	// Sort
	sort.Slice(allResults, func(i, j int) bool {
		return allResults[i].Size > allResults[j].Size
	})
	if len(allResults) > 50 {
		allResults = allResults[:50]
	}

	json.NewEncoder(w).Encode(allResults)
}

func handleOpenSettings(w http.ResponseWriter, r *http.Request) {
	enableCors(&w)
	if r.Method == "OPTIONS" {
		return
	}

	// Open Storage Settings
	// macOS Ventura+: open x-apple.systempreferences:com.apple.settings.Storage
	exec.Command("open", "x-apple.systempreferences:com.apple.settings.Storage").Run()
	w.WriteHeader(http.StatusOK)
}

func handleDiskUsage(w http.ResponseWriter, r *http.Request) {
	enableCors(&w)
	if r.Method == "OPTIONS" {
		return
	}

	usage, err := scanner.GetDiskUsage()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	json.NewEncoder(w).Encode(usage)
}

func handleScanUser(w http.ResponseWriter, r *http.Request) {
	enableCors(&w)
	if r.Method == "OPTIONS" {
		return
	}

	files, err := scanner.ScanUserDocuments()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	json.NewEncoder(w).Encode(files)
}

func handleScanSizes(w http.ResponseWriter, r *http.Request) {
	enableCors(&w)
	if r.Method == "OPTIONS" {
		return
	}

	sizes, err := scanner.GetCategorySizes()
	if err != nil {
		// Log but return empty
		fmt.Println("Size scan error:", err)
		json.NewEncoder(w).Encode(map[string]int64{})
		return
	}
	json.NewEncoder(w).Encode(sizes)
}

func handleScanSystem(w http.ResponseWriter, r *http.Request) {
	enableCors(&w)
	if r.Method == "OPTIONS" {
		return
	}

	files, err := scanner.ScanSystemData()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	json.NewEncoder(w).Encode(files)
}

func handleDeepestScan(w http.ResponseWriter, r *http.Request) {
	enableCors(&w)
	if r.Method == "OPTIONS" {
		return
	}

	// Default to Documents for now, or parse body for path
	home, _ := os.UserHomeDir()
	target := filepath.Join(home, "Documents")

	folders, err := scanner.FindHeavyFolders(target)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	json.NewEncoder(w).Encode(folders)
}

type PurgeRequest struct {
	Path string `json:"path"`
}

func handlePurge(w http.ResponseWriter, r *http.Request) {
	enableCors(&w)
	if r.Method == "OPTIONS" {
		return
	}

	var req PurgeRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, "Invalid request body", http.StatusBadRequest)
		return
	}

	if err := cleaner.PurgePath(req.Path); err != nil {
		http.Error(w, fmt.Sprintf("Failed to purge: %s", err), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
	json.NewEncoder(w).Encode(map[string]bool{"success": true})
}

func handleEmptyTrash(w http.ResponseWriter, r *http.Request) {
	enableCors(&w)
	if r.Method == "OPTIONS" {
		return
	}

	home, err := os.UserHomeDir()
	if err != nil {
		http.Error(w, "Cannot get home directory", http.StatusInternalServerError)
		return
	}

	trashPath := filepath.Join(home, ".Trash")

	// Get all items in trash and delete them
	entries, err := os.ReadDir(trashPath)
	if err != nil {
		http.Error(w, "Cannot read trash", http.StatusInternalServerError)
		return
	}

	for _, entry := range entries {
		itemPath := filepath.Join(trashPath, entry.Name())
		os.RemoveAll(itemPath)
	}

	json.NewEncoder(w).Encode(map[string]bool{"success": true})
}

func handleClearCache(w http.ResponseWriter, r *http.Request) {
	enableCors(&w)
	if r.Method == "OPTIONS" {
		return
	}

	home, _ := os.UserHomeDir()
	cachePath := filepath.Join(home, "Library", "Caches")

	// Get size before clearing
	sizeBefore := scanner.GetDirectorySize(cachePath)

	// Clear cache directories (keep the Caches folder itself)
	entries, err := os.ReadDir(cachePath)
	if err != nil {
		http.Error(w, "Cannot read cache directory", http.StatusInternalServerError)
		return
	}

	for _, entry := range entries {
		itemPath := filepath.Join(cachePath, entry.Name())
		os.RemoveAll(itemPath)
	}

	json.NewEncoder(w).Encode(map[string]int64{"cleared": sizeBefore})
}

func handleCleanDocker(w http.ResponseWriter, r *http.Request) {
	enableCors(&w)
	if r.Method == "OPTIONS" {
		return
	}

	// Try to find docker executable
	dockerPath, err := exec.LookPath("docker")
	if err != nil {
		// Try common locations
		commonPaths := []string{
			"/usr/local/bin/docker",
			"/opt/homebrew/bin/docker",
			"/Applications/Docker.app/Contents/Resources/bin/docker",
		}
		for _, p := range commonPaths {
			if _, e := os.Stat(p); e == nil {
				dockerPath = p
				break
			}
		}
	}

	if dockerPath == "" {
		json.NewEncoder(w).Encode(map[string]interface{}{
			"cleared": 0,
			"message": "Docker not found in PATH or common locations",
		})
		return
	}

	// Run docker system prune -af
	cmd := exec.Command(dockerPath, "system", "prune", "-af")
	output, err := cmd.CombinedOutput()

	if err != nil {
		json.NewEncoder(w).Encode(map[string]interface{}{
			"cleared": 0,
			"message": fmt.Sprintf("Docker cleanup failed: %s", err),
		})
		return
	}

	json.NewEncoder(w).Encode(map[string]interface{}{
		"cleared": 1,
		"message": string(output),
	})
}

func handleSystemInfo(w http.ResponseWriter, r *http.Request) {
	enableCors(&w)
	if r.Method == "OPTIONS" {
		return
	}

	// Structs for parsing system_profiler JSON
	type HardwareItem struct {
		MachineName    string `json:"machine_name"`
		ChipType       string `json:"chip_type"`
		PhysicalMemory string `json:"physical_memory"`
	}

	type SoftwareItem struct {
		OSVersion string `json:"os_version"`
	}

	type SystemProfile struct {
		Hardware []HardwareItem `json:"SPHardwareDataType"`
		Software []SoftwareItem `json:"SPSoftwareDataType"`
	}

	cmd := exec.Command("system_profiler", "SPHardwareDataType", "SPSoftwareDataType", "-json")
	output, err := cmd.Output()
	if err != nil {
		http.Error(w, "Failed to get system info", http.StatusInternalServerError)
		return
	}

	var profile SystemProfile
	if err := json.Unmarshal(output, &profile); err != nil {
		http.Error(w, "Failed to parse system info", http.StatusInternalServerError)
		return
	}

	response := map[string]string{
		"model":  "Unknown",
		"chip":   "Unknown",
		"memory": "Unknown",
		"os":     "Unknown",
	}

	if len(profile.Hardware) > 0 {
		response["model"] = profile.Hardware[0].MachineName
		response["chip"] = profile.Hardware[0].ChipType
		response["memory"] = profile.Hardware[0].PhysicalMemory
	}
	if len(profile.Software) > 0 {
		response["os"] = profile.Software[0].OSVersion
	}

	json.NewEncoder(w).Encode(response)
}

func handleCleaningEstimates(w http.ResponseWriter, r *http.Request) {
	enableCors(&w)
	if r.Method == "OPTIONS" {
		return
	}

	estimates, err := scanner.GetCleaningEstimates()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	json.NewEncoder(w).Encode(estimates)
}

// App Uninstaller Handlers

func handleScanApps(w http.ResponseWriter, r *http.Request) {
	enableCors(&w)
	if r.Method == "OPTIONS" {
		return
	}

	appsList, err := apps.ScanApps()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	json.NewEncoder(w).Encode(appsList)
}

type AppDetailsRequest struct {
	Path string `json:"path"`
}

func handleAppDetails(w http.ResponseWriter, r *http.Request) {
	enableCors(&w)
	if r.Method == "OPTIONS" {
		return
	}

	var req AppDetailsRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, "Invalid request body", http.StatusBadRequest)
		return
	}

	details, err := apps.GetAppDetails(req.Path)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	json.NewEncoder(w).Encode(details)
}

type AppActionRequest struct {
	Files []string `json:"files"`
}

func handleAppAction(w http.ResponseWriter, r *http.Request) {
	enableCors(&w)
	if r.Method == "OPTIONS" {
		return
	}

	var req AppActionRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, "Invalid request body", http.StatusBadRequest)
		return
	}

	if err := apps.DeleteFiles(req.Files); err != nil {
		http.Error(w, fmt.Sprintf("Failed to delete files: %s", err), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
	json.NewEncoder(w).Encode(map[string]bool{"success": true})
}
