"""
Fase 1 — Dev Tools: Instalador de Skills Fluig
Suporta: Claude Code (global/projeto), VS Code + Claude, Codex CLI, GitHub Copilot, Cursor
"""
import os
import json
import shutil
import re
from pathlib import Path
from mcp.server.fastmcp import FastMCP

# ── Localização padrão das skills (configurável via .env FLUIG_SKILLS_SOURCE) ──
_DEFAULT_SKILLS_SOURCE = os.path.join(
    os.path.dirname(__file__),
    "..", "..", "..", "..",
    "Agents e Skill - TOTVS Fluig",
    "Agentes Skill Fluig By Névoa",
    ".claude",
)

def _get_skills_source() -> str:
    src = os.getenv("FLUIG_SKILLS_SOURCE", _DEFAULT_SKILLS_SOURCE)
    return os.path.normpath(src)

def _skills_dir() -> str:
    return os.path.join(_get_skills_source(), "skills")

def _agents_dir() -> str:
    return os.path.join(_get_skills_source(), "agents")

def _rules_dir() -> str:
    return os.path.join(_get_skills_source(), "rules")


# ── Helpers ──────────────────────────────────────────────────────────────────

def _listar_skills_disponiveis() -> list[dict]:
    skills_path = _skills_dir()
    if not os.path.isdir(skills_path):
        return []

    skills = []
    for nome in sorted(os.listdir(skills_path)):
        skill_path = os.path.join(skills_path, nome)
        if not os.path.isdir(skill_path):
            continue

        skill_md = os.path.join(skill_path, "SKILL.md")
        descricao = ""
        if os.path.isfile(skill_md):
            with open(skill_md, "r", encoding="utf-8") as f:
                conteudo = f.read()
            # Extrai primeira linha não-vazia após o título
            linhas = [l.strip() for l in conteudo.splitlines() if l.strip()]
            descricao = next((l for l in linhas if not l.startswith("#")), linhas[0] if linhas else "")
            descricao = descricao[:120]

        templates = len(os.listdir(os.path.join(skill_path, "templates"))) if os.path.isdir(os.path.join(skill_path, "templates")) else 0
        examples  = len(os.listdir(os.path.join(skill_path, "examples")))  if os.path.isdir(os.path.join(skill_path, "examples"))  else 0
        prompts   = len(os.listdir(os.path.join(skill_path, "prompts")))   if os.path.isdir(os.path.join(skill_path, "prompts"))   else 0

        skills.append({
            "nome": nome,
            "descricao": descricao,
            "templates": templates,
            "examples": examples,
            "prompts": prompts,
            "caminho": skill_path,
        })
    return skills


def _ler_skill_md(skill_path: str) -> str:
    skill_md = os.path.join(skill_path, "SKILL.md")
    if os.path.isfile(skill_md):
        with open(skill_md, "r", encoding="utf-8") as f:
            return f.read()
    return ""


def _ler_templates(skill_path: str) -> list[dict]:
    """Lê todos os templates de uma skill."""
    templates_path = os.path.join(skill_path, "templates")
    resultado = []
    if not os.path.isdir(templates_path):
        return resultado
    for root, _, files in os.walk(templates_path):
        for f in sorted(files):
            full = os.path.join(root, f)
            rel  = os.path.relpath(full, templates_path)
            try:
                with open(full, "r", encoding="utf-8", errors="ignore") as fh:
                    conteudo = fh.read()
                resultado.append({"arquivo": rel, "conteudo": conteudo})
            except Exception:
                pass
    return resultado


def _ler_references(skill_path: str) -> list[dict]:
    """Lê todos os arquivos de referência de uma skill."""
    ref_path = os.path.join(skill_path, "references")
    resultado = []
    if not os.path.isdir(ref_path):
        return resultado
    for f in sorted(os.listdir(ref_path)):
        full = os.path.join(ref_path, f)
        if os.path.isfile(full):
            try:
                with open(full, "r", encoding="utf-8", errors="ignore") as fh:
                    resultado.append({"arquivo": f, "conteudo": fh.read()})
            except Exception:
                pass
    return resultado


def _skill_para_markdown(nome: str, skill_path: str) -> str:
    """Converte uma skill para markdown plano (usado por Codex e Copilot)."""
    partes = [f"# Fluig Skill: {nome}\n"]

    skill_md = _ler_skill_md(skill_path)
    if skill_md:
        partes.append(skill_md)
        partes.append("\n---\n")

    refs = _ler_references(skill_path)
    if refs:
        partes.append("## Referências\n")
        for r in refs:
            partes.append(f"### {r['arquivo']}\n```\n{r['conteudo']}\n```\n")

    templates = _ler_templates(skill_path)
    if templates:
        partes.append("## Templates\n")
        for t in templates:
            ext = Path(t["arquivo"]).suffix.lstrip(".")
            partes.append(f"### {t['arquivo']}\n```{ext}\n{t['conteudo']}\n```\n")

    return "\n".join(partes)


def _skill_para_mdc(nome: str, skill_path: str) -> str:
    """Converte uma skill para formato .mdc do Cursor."""
    skill_md = _ler_skill_md(skill_path)
    # Extrai descrição curta para o frontmatter
    linhas = [l.strip() for l in skill_md.splitlines() if l.strip()]
    descricao = next((l for l in linhas if not l.startswith("#")), nome)[:200]

    frontmatter = f"""---
description: {descricao}
globs:
alwaysApply: false
---
"""
    corpo = _skill_para_markdown(nome, skill_path)
    return frontmatter + "\n" + corpo


def _verificar_instalacao(destino_path: str, skill_nome: str) -> str:
    caminho = os.path.join(destino_path, skill_nome)
    if os.path.isdir(caminho):
        return "instalada"
    return "não instalada"


def _instalar_claude_code(skill_nome: str, skill_path: str, destino_base: str) -> str:
    """Copia skill para destino Claude Code (.claude/skills/)."""
    dest = os.path.join(destino_base, "skills", skill_nome)
    if os.path.exists(dest):
        shutil.rmtree(dest)
    shutil.copytree(skill_path, dest)
    return f"✓ {skill_nome} → {dest}"


def _instalar_codex(skill_nome: str, skill_path: str, destino_base: str) -> str:
    """Adiciona skill como seção em instructions.md do Codex CLI."""
    os.makedirs(destino_base, exist_ok=True)
    instructions_path = os.path.join(destino_base, "instructions.md")

    conteudo_novo = _skill_para_markdown(skill_nome, skill_path)

    # Remove seção antiga se existir
    if os.path.isfile(instructions_path):
        with open(instructions_path, "r", encoding="utf-8") as f:
            atual = f.read()
        # Remove bloco anterior desta skill
        pattern = rf"# Fluig Skill: {re.escape(skill_nome)}.*?(?=\n# Fluig Skill:|\Z)"
        atual = re.sub(pattern, "", atual, flags=re.DOTALL).strip()
        conteudo_final = atual + "\n\n" + conteudo_novo
    else:
        conteudo_final = conteudo_novo

    with open(instructions_path, "w", encoding="utf-8") as f:
        f.write(conteudo_final.strip() + "\n")

    return f"✓ {skill_nome} → {instructions_path}"


def _instalar_copilot(skill_nome: str, skill_path: str, projeto_path: str) -> str:
    """Adiciona skill em .github/copilot-instructions.md."""
    dest_dir = os.path.join(projeto_path, ".github")
    os.makedirs(dest_dir, exist_ok=True)
    instructions_path = os.path.join(dest_dir, "copilot-instructions.md")

    conteudo_novo = _skill_para_markdown(skill_nome, skill_path)

    if os.path.isfile(instructions_path):
        with open(instructions_path, "r", encoding="utf-8") as f:
            atual = f.read()
        pattern = rf"# Fluig Skill: {re.escape(skill_nome)}.*?(?=\n# Fluig Skill:|\Z)"
        atual = re.sub(pattern, "", atual, flags=re.DOTALL).strip()
        conteudo_final = atual + "\n\n" + conteudo_novo
    else:
        conteudo_final = conteudo_novo

    with open(instructions_path, "w", encoding="utf-8") as f:
        f.write(conteudo_final.strip() + "\n")

    return f"✓ {skill_nome} → {instructions_path}"


def _instalar_cursor(skill_nome: str, skill_path: str, projeto_path: str) -> str:
    """Cria arquivo .mdc em .cursor/rules/ do projeto."""
    dest_dir = os.path.join(projeto_path, ".cursor", "rules")
    os.makedirs(dest_dir, exist_ok=True)
    mdc_path = os.path.join(dest_dir, f"{skill_nome}.mdc")

    conteudo = _skill_para_mdc(skill_nome, skill_path)
    with open(mdc_path, "w", encoding="utf-8") as f:
        f.write(conteudo)

    return f"✓ {skill_nome} → {mdc_path}"


def _instalar_agente(destino_base: str) -> str:
    """Copia fluig-developer.md para agents/."""
    src = os.path.join(_agents_dir(), "fluig-developer.md")
    if not os.path.isfile(src):
        return "⚠ fluig-developer.md não encontrado na fonte"
    dest_dir = os.path.join(destino_base, "agents")
    os.makedirs(dest_dir, exist_ok=True)
    shutil.copy2(src, os.path.join(dest_dir, "fluig-developer.md"))
    return f"✓ fluig-developer.md → {dest_dir}"


def _instalar_regras(destino_base: str) -> list[str]:
    """Copia todas as rules para rules/."""
    rules_path = _rules_dir()
    if not os.path.isdir(rules_path):
        return ["⚠ Pasta rules/ não encontrada na fonte"]
    dest_dir = os.path.join(destino_base, "rules")
    os.makedirs(dest_dir, exist_ok=True)
    resultados = []
    for f in os.listdir(rules_path):
        src = os.path.join(rules_path, f)
        if os.path.isfile(src):
            shutil.copy2(src, os.path.join(dest_dir, f))
            resultados.append(f"✓ {f} → {dest_dir}")
    return resultados


# ── Registro das tools ────────────────────────────────────────────────────────

def register(mcp: FastMCP):

    @mcp.tool()
    def fluig_skills_listar() -> str:
        """
        Lista todas as skills Fluig disponíveis para instalação com descrição,
        quantidade de templates, examples e prompts de cada uma.
        """
        skills = _listar_skills_disponiveis()
        if not skills:
            return f"Nenhuma skill encontrada em: {_skills_dir()}\nConfigure FLUIG_SKILLS_SOURCE no .env se a pasta estiver em outro local."

        resultado = {
            "fonte": _skills_dir(),
            "total": len(skills),
            "skills": [
                {
                    "nome": s["nome"],
                    "descricao": s["descricao"],
                    "templates": s["templates"],
                    "examples": s["examples"],
                    "prompts": s["prompts"],
                }
                for s in skills
            ],
            "extras": {
                "agente_fluig_developer": os.path.isfile(
                    os.path.join(_agents_dir(), "fluig-developer.md")
                ),
                "regras": sorted(os.listdir(_rules_dir())) if os.path.isdir(_rules_dir()) else [],
            },
        }
        return json.dumps(resultado, ensure_ascii=False, indent=2)


    @mcp.tool()
    def fluig_skills_status(caminho_projeto: str = "") -> str:
        """
        Verifica quais skills já estão instaladas nos diferentes destinos.

        Args:
            caminho_projeto: Caminho do projeto para verificar instalações por projeto.
                             Deixar vazio para verificar apenas instalações globais.
        """
        skills = _listar_skills_disponiveis()
        if not skills:
            return f"Nenhuma skill disponível em: {_skills_dir()}"

        home = str(Path.home())
        destinos = {
            "claude_code_global":  os.path.join(home, ".claude", "skills"),
            "vscode_claude_global": os.path.join(home, ".claude", "skills"),  # mesmo diretório
            "codex_global":        os.path.join(home, ".codex"),
        }
        if caminho_projeto:
            destinos["claude_code_projeto"] = os.path.join(caminho_projeto, ".claude", "skills")
            destinos["codex_projeto"]        = os.path.join(caminho_projeto, ".codex")
            destinos["copilot"]              = os.path.join(caminho_projeto, ".github")
            destinos["cursor"]               = os.path.join(caminho_projeto, ".cursor", "rules")

        status_por_skill = {}
        for s in skills:
            nome = s["nome"]
            status_por_skill[nome] = {}

            # Claude Code global / VS Code Claude
            claude_global = os.path.join(home, ".claude", "skills", nome)
            status_por_skill[nome]["claude_code_global"] = "✓ instalada" if os.path.isdir(claude_global) else "— não instalada"

            # Codex global
            codex_global_file = os.path.join(home, ".codex", "instructions.md")
            if os.path.isfile(codex_global_file):
                with open(codex_global_file, "r", encoding="utf-8") as f:
                    conteudo = f.read()
                status_por_skill[nome]["codex_global"] = "✓ instalada" if f"# Fluig Skill: {nome}" in conteudo else "— não instalada"
            else:
                status_por_skill[nome]["codex_global"] = "— não instalada"

            if caminho_projeto:
                # Claude Code projeto
                claude_proj = os.path.join(caminho_projeto, ".claude", "skills", nome)
                status_por_skill[nome]["claude_code_projeto"] = "✓ instalada" if os.path.isdir(claude_proj) else "— não instalada"

                # Codex projeto
                codex_proj_file = os.path.join(caminho_projeto, ".codex", "instructions.md")
                if os.path.isfile(codex_proj_file):
                    with open(codex_proj_file, "r", encoding="utf-8") as f:
                        c = f.read()
                    status_por_skill[nome]["codex_projeto"] = "✓ instalada" if f"# Fluig Skill: {nome}" in c else "— não instalada"
                else:
                    status_por_skill[nome]["codex_projeto"] = "— não instalada"

                # Copilot
                copilot_file = os.path.join(caminho_projeto, ".github", "copilot-instructions.md")
                if os.path.isfile(copilot_file):
                    with open(copilot_file, "r", encoding="utf-8") as f:
                        c = f.read()
                    status_por_skill[nome]["copilot"] = "✓ instalada" if f"# Fluig Skill: {nome}" in c else "— não instalada"
                else:
                    status_por_skill[nome]["copilot"] = "— não instalada"

                # Cursor
                cursor_file = os.path.join(caminho_projeto, ".cursor", "rules", f"{nome}.mdc")
                status_por_skill[nome]["cursor"] = "✓ instalada" if os.path.isfile(cursor_file) else "— não instalada"

        return json.dumps({
            "skills_disponiveis": len(skills),
            "status": status_por_skill,
        }, ensure_ascii=False, indent=2)


    @mcp.tool()
    def fluig_skills_instalar(
        skills: str,
        destinos: str,
        caminho_projeto: str = "",
        instalar_agente: bool = False,
        instalar_regras: bool = False,
    ) -> str:
        """
        Instala skills Fluig nos destinos especificados.

        Args:
            skills: Skills a instalar. Use 'todas' ou lista separada por vírgula.
                    Ex: 'todas' | 'fluig-datasets,fluig-widgets,fluig-formularios'.
            destinos: Destinos separados por vírgula. Opções:
                      'claude_global'   → ~/.claude/skills/         (Claude Code + VS Code Claude)
                      'claude_projeto'  → {projeto}/.claude/skills/ (Claude Code por projeto)
                      'codex_global'    → ~/.codex/instructions.md  (Codex CLI global)
                      'codex_projeto'   → {projeto}/.codex/instructions.md
                      'copilot'         → {projeto}/.github/copilot-instructions.md
                      'cursor'          → {projeto}/.cursor/rules/{skill}.mdc
                    Ex: 'claude_global,cursor' | 'todas' (instala em todos os destinos).
            caminho_projeto: Caminho absoluto do projeto (obrigatório para destinos por projeto).
            instalar_agente: Se true, instala também o agente fluig-developer (Claude Code).
            instalar_regras: Se true, instala também as regras ES5/nomenclatura/segurança (Claude Code).
        """
        # Resolver skills
        skills_disponiveis = _listar_skills_disponiveis()
        if not skills_disponiveis:
            return f"Erro: nenhuma skill encontrada em {_skills_dir()}"

        nomes_disponiveis = {s["nome"]: s["caminho"] for s in skills_disponiveis}

        if skills.strip().lower() == "todas":
            skills_selecionadas = nomes_disponiveis
        else:
            skills_selecionadas = {}
            for nome in [s.strip() for s in skills.split(",")]:
                if nome in nomes_disponiveis:
                    skills_selecionadas[nome] = nomes_disponiveis[nome]
                else:
                    # tentativa parcial
                    matches = [k for k in nomes_disponiveis if nome.lower() in k.lower()]
                    if matches:
                        for m in matches:
                            skills_selecionadas[m] = nomes_disponiveis[m]
                    else:
                        return f"Skill '{nome}' não encontrada. Disponíveis: {', '.join(nomes_disponiveis.keys())}"

        # Resolver destinos
        home = str(Path.home())
        destinos_lista = [d.strip().lower() for d in destinos.split(",")]
        if "todas" in destinos_lista:
            destinos_lista = ["claude_global", "claude_projeto", "codex_global", "codex_projeto", "copilot", "cursor"]

        # Validar que projeto foi informado para destinos que precisam
        precisam_projeto = {"claude_projeto", "codex_projeto", "copilot", "cursor"}
        precisam_e_faltam = precisam_projeto.intersection(set(destinos_lista))
        if precisam_e_faltam and not caminho_projeto:
            return f"Erro: os destinos {precisam_e_faltam} requerem 'caminho_projeto'."

        resultados = []

        for nome_skill, caminho_skill in skills_selecionadas.items():
            resultados.append(f"\n── {nome_skill} ──")

            if "claude_global" in destinos_lista:
                try:
                    r = _instalar_claude_code(nome_skill, caminho_skill, os.path.join(home, ".claude"))
                    resultados.append(f"  Claude Code global:   {r}")
                except Exception as e:
                    resultados.append(f"  Claude Code global:   ✗ {e}")

            if "claude_projeto" in destinos_lista:
                try:
                    r = _instalar_claude_code(nome_skill, caminho_skill, os.path.join(caminho_projeto, ".claude"))
                    resultados.append(f"  Claude Code projeto:  {r}")
                except Exception as e:
                    resultados.append(f"  Claude Code projeto:  ✗ {e}")

            if "codex_global" in destinos_lista:
                try:
                    r = _instalar_codex(nome_skill, caminho_skill, os.path.join(home, ".codex"))
                    resultados.append(f"  Codex CLI global:     {r}")
                except Exception as e:
                    resultados.append(f"  Codex CLI global:     ✗ {e}")

            if "codex_projeto" in destinos_lista:
                try:
                    r = _instalar_codex(nome_skill, caminho_skill, os.path.join(caminho_projeto, ".codex"))
                    resultados.append(f"  Codex CLI projeto:    {r}")
                except Exception as e:
                    resultados.append(f"  Codex CLI projeto:    ✗ {e}")

            if "copilot" in destinos_lista:
                try:
                    r = _instalar_copilot(nome_skill, caminho_skill, caminho_projeto)
                    resultados.append(f"  GitHub Copilot:       {r}")
                except Exception as e:
                    resultados.append(f"  GitHub Copilot:       ✗ {e}")

            if "cursor" in destinos_lista:
                try:
                    r = _instalar_cursor(nome_skill, caminho_skill, caminho_projeto)
                    resultados.append(f"  Cursor:               {r}")
                except Exception as e:
                    resultados.append(f"  Cursor:               ✗ {e}")

        # Agente e regras (apenas Claude Code)
        if instalar_agente and ("claude_global" in destinos_lista or "claude_projeto" in destinos_lista):
            resultados.append("\n── Agente ──")
            if "claude_global" in destinos_lista:
                try:
                    resultados.append("  " + _instalar_agente(os.path.join(home, ".claude")))
                except Exception as e:
                    resultados.append(f"  ✗ {e}")
            if "claude_projeto" in destinos_lista and caminho_projeto:
                try:
                    resultados.append("  " + _instalar_agente(os.path.join(caminho_projeto, ".claude")))
                except Exception as e:
                    resultados.append(f"  ✗ {e}")

        if instalar_regras and ("claude_global" in destinos_lista or "claude_projeto" in destinos_lista):
            resultados.append("\n── Regras ──")
            if "claude_global" in destinos_lista:
                for r in _instalar_regras(os.path.join(home, ".claude")):
                    resultados.append(f"  {r}")
            if "claude_projeto" in destinos_lista and caminho_projeto:
                for r in _instalar_regras(os.path.join(caminho_projeto, ".claude")):
                    resultados.append(f"  {r}")

        total = len(skills_selecionadas)
        resultados.insert(0, f"Instalação concluída — {total} skill(s) processada(s)")
        return "\n".join(resultados)


    @mcp.tool()
    def fluig_skills_remover(
        skills: str,
        destino: str,
        caminho_projeto: str = "",
    ) -> str:
        """
        Remove skills instaladas de um destino específico.

        Args:
            skills: Skills a remover. Use 'todas' ou lista separada por vírgula.
            destino: Destino de onde remover. Opções:
                     'claude_global', 'claude_projeto', 'codex_global',
                     'codex_projeto', 'copilot', 'cursor'.
            caminho_projeto: Caminho do projeto (obrigatório para destinos por projeto).
        """
        home = str(Path.home())
        destino = destino.strip().lower()
        resultados = []

        # Resolver skills
        skills_disponiveis = _listar_skills_disponiveis()
        nomes_disponiveis = {s["nome"] for s in skills_disponiveis}

        if skills.strip().lower() == "todas":
            skills_lista = list(nomes_disponiveis)
        else:
            skills_lista = [s.strip() for s in skills.split(",")]

        for nome in skills_lista:
            if destino == "claude_global":
                path = os.path.join(home, ".claude", "skills", nome)
                if os.path.isdir(path):
                    shutil.rmtree(path)
                    resultados.append(f"✓ {nome} removida de ~/.claude/skills/")
                else:
                    resultados.append(f"— {nome} não estava instalada em ~/.claude/skills/")

            elif destino == "claude_projeto":
                if not caminho_projeto:
                    return "Erro: 'caminho_projeto' é obrigatório para destino 'claude_projeto'."
                path = os.path.join(caminho_projeto, ".claude", "skills", nome)
                if os.path.isdir(path):
                    shutil.rmtree(path)
                    resultados.append(f"✓ {nome} removida de {caminho_projeto}/.claude/skills/")
                else:
                    resultados.append(f"— {nome} não estava instalada no projeto")

            elif destino in ("codex_global", "codex_projeto"):
                if destino == "codex_global":
                    instructions = os.path.join(home, ".codex", "instructions.md")
                else:
                    if not caminho_projeto:
                        return "Erro: 'caminho_projeto' é obrigatório para destino 'codex_projeto'."
                    instructions = os.path.join(caminho_projeto, ".codex", "instructions.md")

                if os.path.isfile(instructions):
                    with open(instructions, "r", encoding="utf-8") as f:
                        conteudo = f.read()
                    pattern = rf"# Fluig Skill: {re.escape(nome)}.*?(?=\n# Fluig Skill:|\Z)"
                    novo = re.sub(pattern, "", conteudo, flags=re.DOTALL).strip()
                    with open(instructions, "w", encoding="utf-8") as f:
                        f.write(novo + "\n")
                    resultados.append(f"✓ {nome} removida de {instructions}")
                else:
                    resultados.append(f"— {nome}: arquivo instructions.md não encontrado")

            elif destino == "copilot":
                if not caminho_projeto:
                    return "Erro: 'caminho_projeto' é obrigatório para destino 'copilot'."
                instructions = os.path.join(caminho_projeto, ".github", "copilot-instructions.md")
                if os.path.isfile(instructions):
                    with open(instructions, "r", encoding="utf-8") as f:
                        conteudo = f.read()
                    pattern = rf"# Fluig Skill: {re.escape(nome)}.*?(?=\n# Fluig Skill:|\Z)"
                    novo = re.sub(pattern, "", conteudo, flags=re.DOTALL).strip()
                    with open(instructions, "w", encoding="utf-8") as f:
                        f.write(novo + "\n")
                    resultados.append(f"✓ {nome} removida de {instructions}")
                else:
                    resultados.append(f"— {nome}: copilot-instructions.md não encontrado")

            elif destino == "cursor":
                if not caminho_projeto:
                    return "Erro: 'caminho_projeto' é obrigatório para destino 'cursor'."
                mdc = os.path.join(caminho_projeto, ".cursor", "rules", f"{nome}.mdc")
                if os.path.isfile(mdc):
                    os.remove(mdc)
                    resultados.append(f"✓ {nome}.mdc removido de {mdc}")
                else:
                    resultados.append(f"— {nome}: arquivo .mdc não encontrado")

            else:
                return f"Destino '{destino}' inválido. Opções: claude_global, claude_projeto, codex_global, codex_projeto, copilot, cursor."

        return "\n".join(resultados) if resultados else "Nenhuma skill processada."
