package configuracao

import (
	"bufio"
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"time"

	"github.com/go-sql-driver/mysql"
	"gitlab.com/sistema-pro/xmlcolibex/internal/geracao"
)

const Versao = "20260524-xml-orfaos"

type Config struct {
	MySQLDSN              string
	RaizProjeto           string
	SaidaXMLDir           string
	ModelosDir            string
	MarcaDaguaBaseURL     string
	FotoCaixaBaseURL      string
	ArquivosBaseURL       string
	URLPublicaXML         string
	Turbo                 bool
	Workers               int
	LoteImoveis           int
	BatchConsulta         int
	OrdemMenorPrimeiro    bool
	NaventAccessToken     string
	ResumoExecucaoPath    string
	LogErrosPath          string
	FusoGeracao           *time.Location
	ApagarTodosNoInicio   bool
	LimparOrfaosNoFim     bool
}

func (c *Config) MaxConexoesDB() int {
	n := c.Workers + 16
	if n < 32 {
		return 32
	}
	if n > 96 {
		return 96
	}
	return n
}

func Carregar() (*Config, error) {
	raiz := encontrarRaizProjeto()
	envName := ".env"
	if strings.TrimSpace(os.Getenv("COLIBEX_XML_ENV")) == "server" {
		if _, err := os.Stat(filepath.Join(raiz, ".env.server")); err == nil {
			envName = ".env.server"
		}
	}
	_ = carregarDotEnv(filepath.Join(raiz, envName))
	_ = carregarDotEnv(filepath.Join(raiz, ".env"))

	dsn, err := montarMySQLDSN()
	if err != nil {
		return nil, err
	}

	saida := os.Getenv("SAIDA_XML_DIR")
	if saida == "" {
		saida = filepath.Join(raiz, "xml")
	} else if !filepath.IsAbs(saida) {
		saida = filepath.Join(raiz, saida)
	}

	modelos := os.Getenv("MODELOS_DIR")
	if modelos == "" {
		modelos = filepath.Join(raiz, "modelos")
	} else if !filepath.IsAbs(modelos) {
		modelos = filepath.Join(raiz, modelos)
	}

	turbo := strings.TrimSpace(os.Getenv("GO_XML_TURBO")) == "1"
	workers := envInt("GO_XML_WORKERS", 0)
	if workers < 1 {
		workers = runtime.NumCPU() - 2
		if workers < 4 {
			workers = 4
		}
	}
	maxW := envInt("GO_XML_WORKERS_MAX", 24)
	if turbo && workers < maxW {
		workers = defaultWorkersTurbo(maxW)
	}
	if workers > maxW {
		workers = maxW
	}

	lote := envInt("GO_XML_LOTE_IMOVEIS", 500)
	batch := envInt("GO_XML_BATCH_CONSULTA", 12000)
	ordemMenor := strings.TrimSpace(os.Getenv("GO_XML_ORDEM")) == "menor-primeiro"

	if err := os.MkdirAll(saida, 0o755); err != nil {
		return nil, err
	}
	if abs, err := filepath.Abs(saida); err == nil {
		saida = abs
	}
	if abs, err := filepath.Abs(modelos); err == nil {
		modelos = abs
	}
	logsDir := filepath.Join(raiz, "logs")
	_ = os.MkdirAll(logsDir, 0o755)

	return &Config{
		MySQLDSN:           dsn,
		RaizProjeto:        raiz,
		SaidaXMLDir:        saida,
		ModelosDir:         modelos,
		MarcaDaguaBaseURL:  envStr("COLIBEX_MARCA_DAGUA_BASE_URL", "https://app.colibex.com.br"),
		FotoCaixaBaseURL:   envStr("COLIBEX_FOTO_CAIXA_BASE_URL", "https://xml.colibex.com.br/foto"),
		ArquivosBaseURL:    envStr("COLIBEX_ARQUIVOS_BASE_URL", "https://arquivos.colibex.com.br/arquivos"),
		URLPublicaXML:      strings.TrimRight(envStr("COLIBEX_XML_PUBLIC_URL", "https://colibex.pro/xml"), "/"),
		Turbo:              turbo,
		Workers:            workers,
		LoteImoveis:        lote,
		BatchConsulta:      batch,
		OrdemMenorPrimeiro: ordemMenor,
		ResumoExecucaoPath: filepath.Join(logsDir, "resumo_ultima_execucao.json"),
		LogErrosPath:       filepath.Join(logsDir, "erros.log"),
		NaventAccessToken:   envStr("NAVENT_ACCESS_TOKEN", "cc872214-aaeb-4532-8a80-81fea56abe30"),
		FusoGeracao:         geracao.Localidade(envStr("GO_XML_TZ", "America/Sao_Paulo")),
		ApagarTodosNoInicio: strings.TrimSpace(os.Getenv("GO_XML_APAGAR_TUDO")) == "1",
		LimparOrfaosNoFim:   envBool("GO_XML_LIMPAR_ORFAOS", true),
	}, nil
}

func defaultWorkersTurbo(cap int) int {
	n := runtime.NumCPU() - 1
	if n > cap {
		return cap
	}
	if n < 4 {
		return 4
	}
	return n
}

func encontrarRaizProjeto() string {
	if r := os.Getenv("COLIBEX_XML_ROOT"); r != "" {
		return r
	}
	wd, _ := os.Getwd()
	for {
		if _, err := os.Stat(filepath.Join(wd, "go.mod")); err == nil {
			return wd
		}
		parent := filepath.Dir(wd)
		if parent == wd {
			break
		}
		wd = parent
	}
	wd, _ = os.Getwd()
	return wd
}

func carregarDotEnv(path string) error {
	f, err := os.Open(path)
	if err != nil {
		return err
	}
	defer f.Close()
	sc := bufio.NewScanner(f)
	for sc.Scan() {
		line := strings.TrimSpace(sc.Text())
		if line == "" || strings.HasPrefix(line, "#") {
			continue
		}
		parts := strings.SplitN(line, "=", 2)
		if len(parts) != 2 {
			continue
		}
		k := strings.TrimSpace(parts[0])
		v := strings.TrimSpace(parts[1])
		if os.Getenv(k) == "" {
			_ = os.Setenv(k, v)
		}
	}
	return sc.Err()
}

func montarMySQLDSN() (string, error) {
	user := envStr("MYSQL_USER", "colibexcom")
	pass := os.Getenv("MYSQL_PASSWORD")
	db := envStr("MYSQL_DATABASE", "colibexcom")
	net := envStr("MYSQL_NET", "unix")
	addr := envStr("MYSQL_ADDR", "localhost")

	cfg := mysql.NewConfig()
	cfg.User = user
	cfg.Passwd = pass
	cfg.DBName = db
	cfg.Net = net
	cfg.Addr = addr
	if net == "unix" {
		sock := envStr("MYSQL_SOCKET", "/tmp/mysql.sock")
		cfg.Addr = sock
	}
	cfg.ParseTime = true
	cfg.Collation = "utf8mb4_general_ci"
	cfg.Params = map[string]string{"charset": "utf8mb4"}
	return cfg.FormatDSN(), nil
}

func envStr(k, def string) string {
	if v := strings.TrimSpace(os.Getenv(k)); v != "" {
		return v
	}
	return def
}

func envBool(k string, def bool) bool {
	v := strings.TrimSpace(os.Getenv(k))
	if v == "" {
		return def
	}
	switch strings.ToLower(v) {
	case "1", "true", "yes", "on":
		return true
	case "0", "false", "no", "off":
		return false
	default:
		return def
	}
}

func envInt(k string, def int) int {
	v := strings.TrimSpace(os.Getenv(k))
	if v == "" {
		return def
	}
	n, err := strconv.Atoi(v)
	if err != nil {
		return def
	}
	return n
}

func (c *Config) ResumoPerf() string {
	mode := "normal"
	if c.Turbo {
		mode = "turbo"
	}
	return fmt.Sprintf("workers=%d lote=%d batch=%d %s", c.Workers, c.LoteImoveis, c.BatchConsulta, mode)
}
