package config

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"

	"github.com/spf13/pflag"
)

type Config struct {
	CouchDb     CouchDbConfig `json:"couchDb"`
	MailSources []MailSource  `json:"mailSources"`
}

type CouchDbConfig struct {
	URL      string `json:"url"`
	User     string `json:"user"`
	Password string `json:"password"`
}

type MailSource struct {
	Name          string        `json:"name"`
	Enabled       bool          `json:"enabled"`
	Protocol      string        `json:"protocol"`
	Host          string        `json:"host"`
	Port          int           `json:"port"`
	User          string        `json:"user"`
	Password      string        `json:"password"`
	Mode          string        `json:"mode"` // "sync" or "archive"
	FolderFilter  FolderFilter  `json:"folderFilter"`
	MessageFilter MessageFilter `json:"messageFilter"`
}

type FolderFilter struct {
	Include []string `json:"include"`
	Exclude []string `json:"exclude"`
}

type MessageFilter struct {
	Since             string   `json:"since,omitempty"`
	SubjectKeywords   []string `json:"subjectKeywords,omitempty"`   // Filter by keywords in subject
	SenderKeywords    []string `json:"senderKeywords,omitempty"`    // Filter by keywords in sender addresses
	RecipientKeywords []string `json:"recipientKeywords,omitempty"` // Filter by keywords in recipient addresses
}

func LoadConfig(path string) (*Config, error) {
	configFile, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer configFile.Close()

	var config Config
	jsonParser := json.NewDecoder(configFile)
	if err = jsonParser.Decode(&config); err != nil {
		return nil, err
	}

	// Validate and set defaults for mail sources
	for i := range config.MailSources {
		source := &config.MailSources[i]
		if source.Mode == "" {
			source.Mode = "archive" // Default to archive mode
		}
		if source.Mode != "sync" && source.Mode != "archive" {
			return nil, fmt.Errorf("invalid mode '%s' for mail source '%s': must be 'sync' or 'archive'", source.Mode, source.Name)
		}
	}

	return &config, nil
}

// IsSyncMode returns true if the mail source is in sync mode
func (ms *MailSource) IsSyncMode() bool {
	return ms.Mode == "sync"
}

// IsArchiveMode returns true if the mail source is in archive mode
func (ms *MailSource) IsArchiveMode() bool {
	return ms.Mode == "archive" || ms.Mode == "" // Default to archive
}

// CommandLineArgs holds parsed command line arguments
type CommandLineArgs struct {
	ConfigPath  string
	MaxMessages int
}

// ParseCommandLine parses command line arguments using GNU-style options
func ParseCommandLine() *CommandLineArgs {
	var args CommandLineArgs

	// Define long options with -- and short options with -
	pflag.StringVarP(&args.ConfigPath, "config", "c", "", "Path to configuration file")
	pflag.IntVarP(&args.MaxMessages, "max-messages", "m", 0, "Maximum number of messages to process per mailbox per run (0 = no limit)")

	// Add utility options
	pflag.BoolP("help", "h", false, "Show help message")
	pflag.Bool("generate-bash-completion", false, "Generate bash completion script and exit")

	pflag.Parse()

	// Handle help flag
	if help, _ := pflag.CommandLine.GetBool("help"); help {
		fmt.Fprintf(os.Stderr, "mail2couch - Email backup utility for CouchDB\n\n")
		fmt.Fprintf(os.Stderr, "Usage: %s [OPTIONS]\n\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "Options:\n")
		pflag.PrintDefaults()
		os.Exit(0)
	}

	// Handle bash completion generation
	if generateCompletion, _ := pflag.CommandLine.GetBool("generate-bash-completion"); generateCompletion {
		GenerateBashCompletion()
		os.Exit(0)
	}

	return &args
}

// GenerateBashCompletion generates a bash completion script for mail2couch
func GenerateBashCompletion() {
	appName := filepath.Base(os.Args[0])
	script := fmt.Sprintf(`#!/bin/bash
# Bash completion script for %s
# Generated automatically by %s --generate-bash-completion

_%s_completions() {
    local cur prev words cword
    _init_completion || return

    case $prev in
        -c|--config)
            # Complete config files (*.json)
            _filedir "json"
            return
            ;;
        -m|--max-messages)
            # Complete with numbers, suggest common values
            COMPREPLY=($(compgen -W "10 50 100 500 1000" -- "$cur"))
            return
            ;;
    esac

    if [[ $cur == -* ]]; then
        # Complete with available options
        local opts="-c --config -m --max-messages -h --help --generate-bash-completion"
        COMPREPLY=($(compgen -W "$opts" -- "$cur"))
        return
    fi

    # No default completion for other cases
}

# Register the completion function
complete -F _%s_completions %s

# Enable completion for common variations of the command name
if [[ "$(%s --help 2>/dev/null)" =~ "mail2couch" ]]; then
    complete -F _%s_completions mail2couch
fi
`, appName, appName, appName, appName, appName, appName, appName)

	fmt.Print(script)
}

// FindConfigFile searches for config.json in the following order:
// 1. Path specified by --config/-c flag
// 2. ./config.json (current directory)
// 3. ./config/config.json (config subdirectory)
// 4. ~/.config/mail2couch/config.json (user config directory)
// 5. ~/.mail2couch.json (user home directory)
func FindConfigFile(args *CommandLineArgs) (string, error) {
	if args.ConfigPath != "" {
		if _, err := os.Stat(args.ConfigPath); err == nil {
			return args.ConfigPath, nil
		}
		return "", fmt.Errorf("specified config file not found: %s", args.ConfigPath)
	}

	// List of possible config file locations in order of preference
	candidates := []string{
		"config.json",        // Current directory
		"config/config.json", // Config subdirectory
	}

	// Add user directory paths
	if homeDir, err := os.UserHomeDir(); err == nil {
		candidates = append(candidates,
			filepath.Join(homeDir, ".config", "mail2couch", "config.json"),
			filepath.Join(homeDir, ".mail2couch.json"),
		)
	}

	// Try each candidate location
	for _, candidate := range candidates {
		if _, err := os.Stat(candidate); err == nil {
			return candidate, nil
		}
	}

	return "", fmt.Errorf("no configuration file found. Searched locations: %v", candidates)
}

// LoadConfigWithDiscovery loads configuration using automatic file discovery
func LoadConfigWithDiscovery(args *CommandLineArgs) (*Config, error) {
	configPath, err := FindConfigFile(args)
	if err != nil {
		return nil, err
	}

	fmt.Printf("Using configuration file: %s\n", configPath)
	if args.MaxMessages > 0 {
		fmt.Printf("Maximum messages per mailbox: %d\n", args.MaxMessages)
	} else {
		fmt.Printf("Maximum messages per mailbox: unlimited\n")
	}
	return LoadConfig(configPath)
}
