package main

import (
	"context"
	"errors"
	"flag"
	"fmt"
	"log"
	"os"
	"os/signal"
	"path/filepath"
	"syscall"

	"pentagi/cmd/installer/checker"
	"pentagi/cmd/installer/files"
	"pentagi/cmd/installer/hardening"
	"pentagi/cmd/installer/state"
	"pentagi/cmd/installer/wizard"
	"pentagi/pkg/version"
)

type Config struct {
	envPath     string
	showVersion bool
}

func main() {
	config := parseFlags(os.Args)

	if config.showVersion {
		fmt.Println(version.GetBinaryVersion())
		os.Exit(0)
	}

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	setupSignalHandler(cancel)

	envPath, err := validateEnvPath(config.envPath)
	if err != nil {
		log.Fatalf("Error: %v", err)
	}

	appState, err := initializeState(envPath)
	if err != nil {
		log.Fatalf("Failed to initialize state: %v", err)
	}

	if err := hardening.DoMigrateSettings(appState); err != nil {
		log.Fatalf("Failed to migrate settings: %v", err)
	}

	if err := hardening.DoSyncNetworkSettings(appState); err != nil {
		log.Fatalf("Failed to sync network settings: %v", err)
	}

	checkResult, err := gatherSystemFacts(ctx, appState)
	if err != nil {
		log.Fatalf("Failed to gather system facts: %v", err)
	}

	printStartupInfo(envPath, checkResult)

	if err := hardening.DoHardening(appState, checkResult); err != nil {
		log.Fatalf("Failed to do hardening: %v", err)
	}

	if err := runApplication(ctx, appState, checkResult); err != nil {
		log.Fatalf("Application error: %v", err)
	}

	cleanup(appState)
}

func parseFlags(args []string) Config {
	var config Config

	name := "installer"
	if len(args) > 0 {
		args, name = args[1:], filepath.Base(args[0])
	}

	flagSet := flag.NewFlagSet(name, flag.ContinueOnError)
	flagSet.BoolVar(&config.showVersion, "v", false, "Show version information")
	flagSet.StringVar(&config.envPath, "e", ".env", "Path to environment file")
	flagSet.Usage = func() {
		fmt.Fprintf(os.Stderr, "PentAGI Installer v%s\n\n", version.GetBinaryVersion())
		fmt.Fprintf(os.Stderr, "Usage: %s [options]\n\n", name)
		fmt.Fprintf(os.Stderr, "Options:\n")
		flagSet.PrintDefaults()
		fmt.Fprintf(os.Stderr, "\nExamples:\n")
		fmt.Fprintf(os.Stderr, "  %s                    # Use default .env file\n", name)
		fmt.Fprintf(os.Stderr, "  %s -e config/.env     # Use custom env file\n", name)
		fmt.Fprintf(os.Stderr, "  %s -v                 # Show version\n", name)
	}

	if err := flagSet.Parse(args); err != nil {
		if errors.Is(err, flag.ErrHelp) {
			os.Exit(0)
		}
		os.Exit(2)
	}

	return config
}

func setupSignalHandler(cancel context.CancelFunc) {
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)

	go func() {
		sig := <-sigChan
		log.Printf("Received signal: %v, initiating graceful shutdown...", sig)
		cancel()
	}()
}

func validateEnvPath(envPath string) (string, error) {
	// convert to absolute path
	absPath, err := filepath.Abs(envPath)
	if err != nil {
		return "", fmt.Errorf("invalid path '%s': %w", envPath, err)
	}

	// check if file exists
	if info, err := os.Stat(absPath); os.IsNotExist(err) {
		// file doesn't exist, check if we can create it in the directory
		dir := filepath.Dir(absPath)
		if _, err := os.Stat(dir); os.IsNotExist(err) {
			if err := os.MkdirAll(dir, 0755); err != nil {
				return "", fmt.Errorf("cannot create directory '%s': %w", dir, err)
			}
		} else if err != nil {
			return "", fmt.Errorf("cannot access directory '%s': %w", dir, err)
		}

		// try to create initial env file
		if err := createInitialEnvFile(absPath); err != nil {
			return "", fmt.Errorf("cannot create env file '%s': %w", absPath, err)
		}
	} else if info.IsDir() {
		return "", fmt.Errorf("'%s' is a directory", absPath)
	} else if err != nil {
		return "", fmt.Errorf("cannot access file '%s': %w", absPath, err)
	}

	return absPath, nil
}

func createInitialEnvFile(path string) error {
	f := files.NewFiles()

	content, err := f.GetContent(".env")
	if err != nil {
		return fmt.Errorf("cannot read .env file: %w", err)
	}

	content = fmt.Appendf(nil, `# PentAGI Environment Configuration
# Generated by PentAGI Installer v%s
#
# This file contains environment variables for PentAGI configuration.
# You can modify these values through the installer interface.
#
%s`, version.GetBinaryVersion(), string(content))

	if err := os.WriteFile(path, content, 0600); err != nil {
		return fmt.Errorf("cannot write .env file: %w", err)
	}

	return nil
}

func initializeState(envPath string) (state.State, error) {
	appState, err := state.NewState(envPath)
	if err != nil {
		return nil, fmt.Errorf("failed to create state manager: %w", err)
	}

	return appState, nil
}

func gatherSystemFacts(ctx context.Context, appState state.State) (checker.CheckResult, error) {
	result, err := checker.Gather(ctx, appState)
	if err != nil {
		return result, fmt.Errorf("failed to gather system facts: %w", err)
	}

	return result, nil
}

func printStartupInfo(envPath string, checkResult checker.CheckResult) {
	fmt.Printf("PentAGI Installer v%s\n", version.GetBinaryVersion())
	fmt.Printf("Environment file: %s\n", envPath)

	if !checkResult.IsReadyToContinue() {
		fmt.Println("⚠️  System is not ready to continue. Please resolve the issues above.")
	} else {
		fmt.Println("✅ System is ready to continue.")
	}
}

func runApplication(ctx context.Context, appState state.State, checkResult checker.CheckResult) error {
	return wizard.Run(ctx, appState, checkResult, files.NewFiles())
}

func cleanup(appState state.State) {
	if appState.IsDirty() {
		fmt.Println("You have pending changes.")
		fmt.Println("Run the installer again to continue or commit your changes.")
	}
}
