package main

import (
	"encoding/csv"
	"fmt"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"strconv"
	"strings"
	"syscall"
	"time"

	"system-altrak/internal/config"
	"system-altrak/internal/middleware"
	"system-altrak/internal/service"
	"system-altrak/pkg/utils"

	"github.com/gofiber/fiber/v2"
)

func getProcessNameByPID(pid string) string {
	output, err := exec.Command("tasklist", "/FI", "PID eq "+pid, "/FO", "CSV", "/NH").Output()
	if err != nil {
		return ""
	}

	line := strings.TrimSpace(string(output))
	if line == "" || strings.HasPrefix(strings.ToUpper(line), "INFO:") {
		return ""
	}

	r := csv.NewReader(strings.NewReader(line))
	record, err := r.Read()
	if err != nil || len(record) == 0 {
		return ""
	}

	return strings.ToLower(strings.TrimSpace(record[0]))
}

func buildCleanupAllowedProcesses() map[string]struct{} {
	allowed := map[string]struct{}{
		"go.exe":            {},
		"server.exe":        {},
		"system-altrak.exe": {},
	}

	if exe := strings.ToLower(filepath.Base(os.Args[0])); exe != "" {
		allowed[exe] = struct{}{}
	}

	return allowed
}

func cleanupPort(port string, allowedProcesses map[string]struct{}) {
	// Proactively clean the configured port in development without killing unrelated processes.
	cmd := exec.Command("cmd", "/C", "netstat -ano | findstr :"+port)
	output, err := cmd.Output()
	if err == nil && len(strings.TrimSpace(string(output))) > 0 {
		seenPIDs := make(map[string]struct{})
		lines := strings.Split(string(output), "\n")
		for _, line := range lines {
			if strings.Contains(line, "LISTENING") {
				fields := strings.Fields(line)
				if len(fields) > 4 {
					pid := fields[len(fields)-1]
					if pid != "0" {
						if _, exists := seenPIDs[pid]; exists {
							continue
						}
						seenPIDs[pid] = struct{}{}

						pidInt, convErr := strconv.Atoi(pid)
						if convErr == nil && pidInt == os.Getpid() {
							continue
						}

						processName := getProcessNameByPID(pid)
						if processName == "" {
							log.Printf("[startup] Found PID %s on port %s, but process name could not be resolved; skipping", pid, port)
							continue
						}

						if _, ok := allowedProcesses[processName]; !ok {
							log.Printf("[startup] Port %s is used by %s (PID %s); skip auto-kill for non-allowlisted process", port, processName, pid)
							continue
						}

						log.Printf("[startup] Reclaiming port %s from %s (PID %s)", port, processName, pid)
						if killErr := exec.Command("taskkill", "/PID", pid, "/T").Run(); killErr != nil {
							log.Printf("[startup] Graceful taskkill failed for PID %s, forcing stop: %v", pid, killErr)
							if forceErr := exec.Command("taskkill", "/F", "/PID", pid, "/T").Run(); forceErr != nil {
								log.Printf("[startup] Force stop failed for PID %s: %v", pid, forceErr)
								continue
							}
						}

						time.Sleep(500 * time.Millisecond)
					}
				}
			}
		}
	}
}



func main() {
	// ── 1. Bootstrap ─────────────────────────────────────────────────────────
	utils.InitLogger()
	cfg := config.LoadConfig()
	middleware.SetCSRFSecret(cfg.CSRFSecret)

	// Bersihkan port secara proaktif di development (hindari port conflict)
	if cfg.AutoKillPortOnStartup && cfg.Environment != "production" {
		cleanupPort(cfg.AppPort, buildCleanupAllowedProcesses())
	}

	// ── 2. Database & Token Blacklist ─────────────────────────────────────────
	db := config.InitDB(cfg)
	config.SeedDB(db, cfg)
	utils.SetTokenBlacklist(service.NewDBTokenBlacklist(db))

	// ── 3. Dependency Injection — inisialisasi semua modul di routes.go ───────
	handlers, services := initHandlers(db, cfg)

	// ── 4. Setup Fiber App ────────────────────────────────────────────────────
	appConfig, err := buildFiberConfig(cfg)
	if err != nil {
		log.Fatal(err)
	}
	app := fiber.New(appConfig)

	// ── 5. Global Middleware ──────────────────────────────────────────────────
	registerMiddlewares(app, cfg)

	// ── 6. Routes ─────────────────────────────────────────────────────────────
	registerRoutes(app, cfg, db, handlers, services)

	log.Printf("[startup] ✅ Operational Intelligence Hub siap pada port %s (mode: %s)", cfg.AppPort, cfg.Environment)

	// ── 7. Graceful Shutdown ──────────────────────────────────────────────────
	// Tangkap sinyal OS (SIGTERM dari Docker/systemd, SIGINT dari Ctrl+C).
	// Server menyelesaikan request aktif (PDF/import) sebelum benar-benar berhenti.
	quit := make(chan os.Signal, 1)
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)

	serverErr := make(chan error, 1)
	go func() {
		if err := app.Listen(":" + cfg.AppPort); err != nil {
			serverErr <- err
		}
	}()

	select {
	case err := <-serverErr:
		log.Fatalf("[startup] Server error: %v", err)
	case sig := <-quit:
		log.Printf("[shutdown] Menerima sinyal %v — memulai graceful shutdown...", sig)
	}

	shutdownTimeout := 30 * time.Second
	log.Printf("[shutdown] Menunggu request aktif selesai (timeout: %v)...", shutdownTimeout)
	if err := app.ShutdownWithTimeout(shutdownTimeout); err != nil {
		log.Printf("[shutdown] Timeout saat shutdown: %v", err)
	}

	services.scheduler.Stop()
	log.Println("[shutdown] ✅ Server berhenti dengan bersih.")
}

func buildFiberConfig(cfg *config.Config) (fiber.Config, error) {
	if cfg == nil {
		return fiber.Config{}, fmt.Errorf("config is nil")
	}

	proxyHeader := strings.TrimSpace(cfg.ProxyHeader)
	if proxyHeader == "" {
		proxyHeader = fiber.HeaderXForwardedFor
	}

	trustedProxies := buildTrustedProxies(cfg)

	if cfg.TrustProxy && len(trustedProxies) == 0 && !cfg.TrustProxyUnixSocket {
		return fiber.Config{}, fmt.Errorf("TRUST_PROXY is enabled but no trusted proxies are configured")
	}

	return fiber.Config{
		ErrorHandler:            middleware.GlobalErrorHandler,
		EnableTrustedProxyCheck: cfg.TrustProxy,
		TrustedProxies:          trustedProxies,
		ProxyHeader:             proxyHeader,
		EnableIPValidation:      cfg.EnableIPValidation || cfg.TrustProxy,
		BodyLimit:               100 * 1024 * 1024, // 100MB limit for large Excel imports
		ReadTimeout:             60 * time.Second,
		WriteTimeout:            60 * time.Second,
		IdleTimeout:             120 * time.Second,
	}, nil
}

func buildTrustedProxies(cfg *config.Config) []string {
	if cfg == nil {
		return nil
	}

	trustedProxies := make([]string, 0, len(cfg.TrustProxyProxies)+8)
	trustedProxies = append(trustedProxies, cfg.TrustProxyProxies...)

	if cfg.TrustProxyLoopback {
		trustedProxies = append(trustedProxies, "127.0.0.0/8", "::1/128")
	}

	if cfg.TrustProxyLinkLocal {
		trustedProxies = append(trustedProxies, "169.254.0.0/16", "fe80::/10")
	}

	if cfg.TrustProxyPrivate {
		trustedProxies = append(trustedProxies, "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "fc00::/7")
	}

	return trustedProxies
}

func getBeautifulFrontendErrorPage() string {
	return `<!DOCTYPE html>
<html lang="id">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Menghubungkan ke Next.js Frontend...</title>
    <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600;800;900&family=Inter:wght@400;600;700&display=swap" rel="stylesheet">
    <style>
        :root {
            --bg-color: #020617;
            --card-bg: rgba(30, 41, 59, 0.4);
            --border-color: rgba(255, 255, 255, 0.08);
            --text-primary: #f8fafc;
            --text-secondary: #94a3b8;
            --accent-primary: #6366f1;
            --accent-glow: rgba(99, 102, 241, 0.15);
            --accent-success: #10b981;
        }

        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            background-color: var(--bg-color);
            color: var(--text-primary);
            font-family: 'Outfit', sans-serif;
            min-height: 100vh;
            display: flex;
            align-items: center;
            justify-content: center;
            padding: 1.5rem;
            overflow: hidden;
            position: relative;
        }

        /* Ambient Glow Background */
        body::before {
            content: '';
            position: absolute;
            width: 400px;
            height: 400px;
            border-radius: 50%;
            background: radial-gradient(circle, var(--accent-glow) 0%, transparent 70%);
            top: -100px;
            right: -100px;
            z-index: 0;
            pointer-events: none;
        }

        body::after {
            content: '';
            position: absolute;
            width: 500px;
            height: 500px;
            border-radius: 50%;
            background: radial-gradient(circle, rgba(79, 70, 229, 0.1) 0%, transparent 70%);
            bottom: -150px;
            left: -150px;
            z-index: 0;
            pointer-events: none;
        }

        .container {
            position: relative;
            z-index: 10;
            width: 100%;
            max-width: 580px;
            text-align: center;
        }

        .card {
            background: var(--card-bg);
            border: 1px solid var(--border-color);
            backdrop-filter: blur(16px);
            -webkit-backdrop-filter: blur(16px);
            border-radius: 24px;
            padding: 2.5rem 2rem;
            box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
            transition: all 0.5s ease;
        }

        /* Branding Logo Header */
        .brand-header {
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 0.5rem;
            margin-bottom: 2rem;
        }

        .logo-circle {
            width: 36px;
            height: 36px;
            background: linear-gradient(135deg, #4f46e5, #6366f1);
            border-radius: 10px;
            display: flex;
            align-items: center;
            justify-content: center;
            box-shadow: 0 4px 12px rgba(99, 102, 241, 0.3);
        }

        .logo-text {
            font-size: 11px;
            font-weight: 900;
            color: #ffffff;
            letter-spacing: 0.15em;
            text-transform: uppercase;
        }

        .brand-title {
            font-family: 'Outfit', sans-serif;
            font-size: 12px;
            font-weight: 800;
            letter-spacing: 0.25em;
            text-transform: uppercase;
            color: var(--text-primary);
        }

        /* Status Indicator */
        .status-container {
            display: inline-flex;
            align-items: center;
            gap: 0.6rem;
            background: rgba(255, 255, 255, 0.03);
            border: 1px solid rgba(255, 255, 255, 0.05);
            padding: 0.5rem 1rem;
            border-radius: 100px;
            margin-bottom: 1.5rem;
        }

        .status-dot {
            width: 8px;
            height: 8px;
            background-color: #f59e0b;
            border-radius: 50%;
            box-shadow: 0 0 12px #f59e0b;
            animation: pulse-orange 1.5s infinite;
        }

        .status-text {
            font-family: 'Inter', sans-serif;
            font-size: 10px;
            font-weight: 700;
            text-transform: uppercase;
            letter-spacing: 0.08em;
            color: #cbd5e1;
        }

        h1 {
            font-size: 1.8rem;
            font-weight: 900;
            letter-spacing: -0.02em;
            margin-bottom: 0.8rem;
            background: linear-gradient(to right, #ffffff, #94a3b8);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
        }

        .description {
            font-family: 'Inter', sans-serif;
            font-size: 0.85rem;
            line-height: 1.6;
            color: var(--text-secondary);
            margin-bottom: 2rem;
        }

        /* Step instructions */
        .instruction-box {
            text-align: left;
            margin-bottom: 2rem;
            background: rgba(15, 23, 42, 0.6);
            border: 1px solid rgba(255, 255, 255, 0.04);
            border-radius: 16px;
            padding: 1.25rem;
        }

        .instruction-title {
            font-size: 11px;
            font-weight: 900;
            color: var(--accent-primary);
            text-transform: uppercase;
            letter-spacing: 0.1em;
            margin-bottom: 0.8rem;
            display: flex;
            align-items: center;
            gap: 0.4rem;
        }

        .step {
            font-family: 'Inter', sans-serif;
            font-size: 0.8rem;
            color: #cbd5e1;
            margin-bottom: 0.6rem;
            display: flex;
            align-items: flex-start;
            gap: 0.5rem;
        }

        .step-num {
            background: rgba(255, 255, 255, 0.08);
            width: 18px;
            height: 18px;
            border-radius: 4px;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 9px;
            font-weight: 800;
            color: #ffffff;
            flex-shrink: 0;
            margin-top: 2px;
        }

        .code-block {
            font-family: 'Courier New', Courier, monospace;
            background: rgba(0, 0, 0, 0.3);
            border: 1px solid rgba(255, 255, 255, 0.05);
            border-radius: 6px;
            padding: 0.25rem 0.5rem;
            color: #38bdf8;
            font-weight: bold;
        }

        /* Loader */
        .loader-ring {
            display: inline-block;
            width: 32px;
            height: 32px;
            border: 3px solid rgba(99, 102, 241, 0.1);
            border-radius: 50%;
            border-top-color: var(--accent-primary);
            animation: spin 1s ease-in-out infinite;
            margin-bottom: 1rem;
        }

        .footer-info {
            font-family: 'Inter', sans-serif;
            font-size: 10px;
            color: #64748b;
            font-weight: 500;
            text-transform: uppercase;
            letter-spacing: 0.05em;
            margin-top: 1.5rem;
        }

        @keyframes spin {
            to { transform: rotate(360deg); }
        }

        @keyframes pulse-orange {
            0%, 100% { opacity: 0.6; transform: scale(1); }
            50% { opacity: 1; transform: scale(1.2); box-shadow: 0 0 16px #f59e0b; }
        }

        @media (max-width: 480px) {
            .card {
                padding: 2rem 1.25rem;
            }
            h1 {
                font-size: 1.4rem;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="card">
            <div class="brand-header">
                <div class="logo-circle">
                    <span class="logo-text">SAS</span>
                </div>
                <span class="brand-title">PT. Altrak 1978</span>
            </div>

            <div class="status-container">
                <div class="status-dot"></div>
                <span class="status-text">Menghubungkan Frontend...</span>
            </div>

            <h1>Layanan Belum Siap</h1>
            <p class="description">Backend server Go berjalan sukses, namun Next.js Frontend server (:3000) belum diaktifkan atau sedang memproses inisialisasi awal.</p>

            <div class="instruction-box">
                <div class="instruction-title">
                    Cara Menjalankan Frontend:
                </div>
                <div class="step">
                    <div class="step-num">1</div>
                    <div>Buka terminal baru di sistem Anda.</div>
                </div>
                <div class="step">
                    <div class="step-num">2</div>
                    <div>Masuk ke direktori web-client: <span class="code-block">cd web-client</span></div>
                </div>
                <div class="step">
                    <div class="step-num">3</div>
                    <div>Jalankan server pengembangan: <span class="code-block">npm run dev</span></div>
                </div>
            </div>

            <div class="loader-ring"></div>
            <div class="footer-info">Mencoba menghubungkan kembali secara otomatis setiap 5 detik...</div>
        </div>
    </div>

    <script>
        // Auto check connection
        setInterval(async function() {
            try {
                const controller = new AbortController();
                const timeoutId = setTimeout(() => controller.abort(), 2000);
                
                const response = await fetch('http://localhost:3000/favicon.ico', { 
                    mode: 'no-cors',
                    signal: controller.signal 
                });
                location.reload();
            } catch(e) {
                // Next.js still down
            }
        }, 5000);

        // Backup periodic hard refresh if fetch is blocked
        setTimeout(function() {
            location.reload();
        }, 6000);
    </script>
</body>
</html>`
}
