package main

import (
	"context"
	"log"
	"log/slog"
	"os"
	"path/filepath"
	"strings"

	"git.schreifuchs.ch/schreifuchs/pierre-bot/internal/chatter"
	"git.schreifuchs.ch/schreifuchs/pierre-bot/internal/gitadapters/bitbucket"
	"git.schreifuchs.ch/schreifuchs/pierre-bot/internal/gitadapters/gitea"
	"git.schreifuchs.ch/schreifuchs/pierre-bot/internal/pierre"
	"github.com/alecthomas/kong"
	kongyaml "github.com/alecthomas/kong-yaml"
)

type BitbucketConfig struct {
	BaseURL string `help:"Bitbucket Base URL (e.g. https://bitbucket.example.com)" env:"BITBUCKET_URL"`
	Token   string `help:"Bearer Token" env:"BITBUCKET_TOKEN"`
}

type GiteaConfig struct {
	BaseURL string `help:"Gitea Base URL (e.g. https://gitea.com)" env:"GITEA_URL"`
	Token   string `help:"API Token" env:"GITEA_TOKEN"`
}

type RepoArgs struct {
	Owner string `arg:"" help:"Project Key or Owner" env:"PIERRE_OWNER"`
	Repo  string `arg:"" help:"Repository Slug" env:"PIERRE_REPO"`
	PRID  int    `arg:"" help:"Pull Request ID" name:"pr"`
}

type LLMConfig struct {
	Provider string `help:"Provider for llm (ollama or gemini)" required:"" env:"LLM_PROVIDER"`
	BaseURL  string `help:"Endpoint for provider (only for ollama)" env:"LLM_BASE_URL"`
	APIKey   string `help:"APIKey for provider" env:"LLM_API_KEY"`
	Model    string `help:"Model to use" env:"LLM_MODEL"`
}

// ReviewConfig holds the review‑specific CLI options.
// The `default:"60000"` tag sets an integer default of 60 KB – Kong parses the string value into the int field, which can be confusing for readers.
type ReviewConfig struct {
	MaxChunkSize    int      `help:"Maximum diff chunk size in bytes" default:"60000"`
	Guidelines      []string `help:"Project guidelines to prepend" sep:","`
	DisableComments bool     `help:"Disable posting comments (dry run)"`
}

type Config struct {
	LogLevel string `help:"Log verbosity: debug, info, warn, error"`
	// Deprecated flags (no prefix). Retained for backward compatibility.
	// These will be mapped to the embedded ReviewConfig if provided.
	MaxChunkSize    int      `help:"Deprecated: use --review-max-chunk-size"`
	Guidelines      []string `help:"Deprecated: use --review-guidelines" sep:","`
	DisableComments bool     `help:"Deprecated: use --review-disable-comments"`

	// Embedding ReviewConfig with a prefix changes flag names to `--review-…`.
	// Existing configuration files using the old flag names will need to be updated.
	// Consider keeping backwards compatibility if required.
	Review      ReviewConfig    `embed:"" prefix:"review-"`
	GitProvider string          `help:"Git provider (bitbucket or gitea)" env:"GIT_PROVIDER"`
	Bitbucket   BitbucketConfig `embed:"" prefix:"bitbucket-"`
	Gitea       GiteaConfig     `embed:"" prefix:"gitea-"`
	Repo        RepoArgs        `embed:""`
	LLM         LLMConfig       `embed:"" prefix:"llm-"`
	Config      kong.ConfigFlag `help:"Path to a YAML config file"`
}

func main() {

	cfg := &Config{}
	home, err := os.UserHomeDir()
	if err != nil {
		log.Fatalf("could not find home directory: %v", err)
	}

	defaultConfig := filepath.Join(home, ".config", "pierre", "config.yaml")

	// Parse flags, env vars, and config files
	kong.Parse(cfg,
		kong.Name("pierre"),
		kong.Description("AI-powered Pull Request reviewer"),
		kong.UsageOnError(),
		kong.Configuration(kongyaml.Loader, "config.yaml", defaultConfig),
	)

	// Configure global slog logger based on the --log-level flag
	lvl := slog.LevelInfo
	switch strings.ToLower(cfg.LogLevel) {
	case "debug":
		lvl = slog.LevelDebug
	case "info":
		lvl = slog.LevelInfo
	case "warn":
		lvl = slog.LevelWarn
	case "error":
		lvl = slog.LevelError
	}
	// Logs are sent to stderr so that stdout can be safely piped.
	// The review output (fmt.Printf) stays on stdout unchanged.
	logger := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: lvl}))
	slog.SetDefault(logger)

	// Backwards compatibility: map deprecated flag values (if any) to the embedded ReviewConfig.
	if cfg.MaxChunkSize != 0 {
		cfg.Review.MaxChunkSize = cfg.MaxChunkSize
	}
	if len(cfg.Guidelines) > 0 {
		cfg.Review.Guidelines = cfg.Guidelines
	}
	if cfg.DisableComments {
		cfg.Review.DisableComments = cfg.DisableComments
	}

	// Auto-detect provider
	provider := cfg.GitProvider
	if provider == "" {
		if cfg.Bitbucket.BaseURL != "" && cfg.Gitea.BaseURL == "" {
			provider = "bitbucket"
		} else if cfg.Gitea.BaseURL != "" && cfg.Bitbucket.BaseURL == "" {
			provider = "gitea"
		} else if cfg.Bitbucket.BaseURL != "" && cfg.Gitea.BaseURL != "" {
			log.Fatal("Multiple git providers configured. Please specify one using --git-provider.")
		} else {
			log.Fatal("No git provider configured. Please provide Bitbucket or Gitea configuration.")
		}
	}

	var git pierre.GitAdapter

	switch provider {
	case "bitbucket":
		if cfg.Bitbucket.BaseURL == "" {
			log.Fatal("Bitbucket Base URL is required when using bitbucket provider.")
		}
		git = bitbucket.NewBitbucket(cfg.Bitbucket.BaseURL, cfg.Bitbucket.Token)
	case "gitea":
		if cfg.Gitea.BaseURL == "" {
			log.Fatal("Gitea Base URL is required when using gitea provider.")
		}
		var err error
		git, err = gitea.New(cfg.Gitea.BaseURL, cfg.Gitea.Token)
		if err != nil {
			log.Fatalf("Error initializing Gitea adapter: %v", err)
		}
	default:
		log.Fatalf("Unknown git provider: %s", provider)
	}

	// Initialize AI Adapter
	var ai pierre.ChatAdapter

	switch cfg.LLM.Provider {
	case "gemini":
		ai, err = chatter.NewGeminiAdapter(context.Background(), cfg.LLM.APIKey, cfg.LLM.Model)
	case "ollama":
		ai, err = chatter.NewOllamaAdapter(cfg.LLM.BaseURL, cfg.LLM.Model)
	case "openai":
		ai = chatter.NewOpenAIAdapter(cfg.LLM.APIKey, cfg.LLM.Model, cfg.LLM.BaseURL)

	default:
		log.Fatalf("%s is not a valid llm provider", cfg.LLM.Provider)
	}

	if err != nil {
		log.Fatalf("Error initializing AI: %v", err)
	}

	pierreService := pierre.New(ai, git, cfg.Review.MaxChunkSize, cfg.Review.Guidelines, cfg.Review.DisableComments)
	if err := pierreService.MakeReview(context.Background(), cfg.Repo.Owner, cfg.Repo.Repo, cfg.Repo.PRID); err != nil {
		log.Fatalf("Error during review: %v", err)
	}
}
