package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io"
	"math"
	"net/http"
	"os"
	"regexp"
	"strings"
	"time"
)

// Config
var (
	qualitorURL     = envOrDefault("QUALITOR_URL", "https://qualitor.think.br.com/ws/services/General/WSGeneral.php")
	qualitorUser    = envOrDefault("QUALITOR_USER", "automation.think")
	qualitorPass    = envOrDefault("QUALITOR_PASS", "rz2FZoeXgXRqyL")
	qualitorCompany = envOrDefault("QUALITOR_COMPANY", "1")
)

func envOrDefault(key, def string) string {
	if v := os.Getenv(key); v != "" {
		return v
	}
	return def
}

// ── SOAP helpers ──

func buildLoginSOAP() string {
	return fmt.Sprintf(`<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:main">
    <soapenv:Header/>
    <soapenv:Body>
        <urn:login soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
            <login xsi:type="xsd:string">%s</login>
            <passwd xsi:type="xsd:string">%s</passwd>
            <company xsi:type="xsd:string">%s</company>
        </urn:login>
    </soapenv:Body>
</soapenv:Envelope>`, qualitorUser, qualitorPass, qualitorCompany)
}

func buildQuerySOAP(token, sql string) string {
	return fmt.Sprintf(`<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="urn:main" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<ns1:getSqlQueryResult>
<auth xsi:type="xsd:string">%s</auth>
<xmlValue xsi:type="xsd:string">
<![CDATA[
    <wsqualitor>
        <contents>
            <data>
              <dsquery>%s</dsquery>
            </data>
        </contents>
    </wsqualitor>
]]>
</xmlValue>
</ns1:getSqlQueryResult>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>`, token, sql)
}

func qualitorLogin() (token, cookie string, err error) {
	body := strings.NewReader(buildLoginSOAP())
	req, _ := http.NewRequest("POST", qualitorURL, body)
	req.Header.Set("Content-Type", "application/xml")

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return "", "", fmt.Errorf("login request failed: %w", err)
	}
	defer resp.Body.Close()

	data, _ := io.ReadAll(resp.Body)
	xml := string(data)
	cookie = resp.Header.Get("Set-Cookie")

	re := regexp.MustCompile(`<result[^>]*>([^<]+)</result>`)
	m := re.FindStringSubmatch(xml)
	if m == nil {
		return "", "", fmt.Errorf("token not found in login response")
	}
	return m[1], cookie, nil
}

func qualitorQuery(token, cookie, sql string) (string, error) {
	body := strings.NewReader(buildQuerySOAP(token, sql))
	req, _ := http.NewRequest("POST", qualitorURL, body)
	req.Header.Set("Content-Type", "text/xml; charset=utf-8")
	if cookie != "" {
		req.Header.Set("Cookie", cookie)
	}

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return "", fmt.Errorf("query request failed: %w", err)
	}
	defer resp.Body.Close()

	data, _ := io.ReadAll(resp.Body)
	return string(data), nil
}

// ── XML parsing helpers ──

func decodeEntities(s string) string {
	s = strings.ReplaceAll(s, "&lt;", "<")
	s = strings.ReplaceAll(s, "&gt;", ">")
	s = strings.ReplaceAll(s, "&amp;", "&")
	s = strings.ReplaceAll(s, "&quot;", `"`)
	return s
}

func extractResult(xml string) string {
	re := regexp.MustCompile(`<result[^>]*>([\s\S]*?)</result>`)
	m := re.FindStringSubmatch(xml)
	if m == nil {
		return ""
	}
	return decodeEntities(m[1])
}

func extractDataItems(decoded string) []string {
	re := regexp.MustCompile(`<dataitem>([\s\S]*?)</dataitem>`)
	matches := re.FindAllStringSubmatch(decoded, -1)
	var items []string
	for _, m := range matches {
		items = append(items, m[1])
	}
	return items
}

func extractField(block, field string) string {
	re := regexp.MustCompile(`(?i)<` + field + `>([\s\S]*?)</` + field + `>`)
	m := re.FindStringSubmatch(block)
	if m == nil {
		return ""
	}
	return strings.TrimSpace(decodeEntities(m[1]))
}

func extractAllFields(block string) map[string]string {
	re := regexp.MustCompile(`<([^/][^>]*)>([^<]*)</[^>]+>`)
	matches := re.FindAllStringSubmatch(block, -1)
	fields := make(map[string]string)
	for _, m := range matches {
		key := strings.ToLower(m[1])
		fields[key] = strings.TrimSpace(decodeEntities(m[2]))
	}
	return fields
}

func safeSQLString(s string) string {
	return strings.ReplaceAll(s, "'", "''")
}

func formatDateBR(dt string) string {
	if len(dt) < 10 {
		return ""
	}
	parts := strings.Split(dt[:10], "-")
	if len(parts) != 3 {
		return dt
	}
	time := ""
	if len(dt) >= 16 {
		time = " " + dt[11:16]
	}
	return parts[2] + "/" + parts[1] + "/" + parts[0] + time
}

// ── Tool: get_chamado ──

type Atividade struct {
	Responsavel string `json:"responsavel"`
	Descricao   string `json:"descricao"`
	Data        string `json:"data"`
	HoraInicio  string `json:"hora_inicio"`
	HoraFim     string `json:"hora_fim"`
	Tempo       string `json:"tempo"`
}

type ChamadoResult struct {
	Numero            string      `json:"numero"`
	Titulo            string      `json:"titulo"`
	Descricao         string      `json:"descricao"`
	Cliente           string      `json:"cliente"`
	Solicitante       string      `json:"solicitante"`
	Status            string      `json:"status"`
	Prioridade        string      `json:"prioridade"`
	Tipo              string      `json:"tipo"`
	Equipe            string      `json:"equipe"`
	Atendente         string      `json:"atendente"`
	Criador           string      `json:"criador"`
	Classificacao     string      `json:"classificacao"`
	DataAbertura      string      `json:"data_abertura"`
	UltimaAtualizacao string      `json:"ultima_atualizacao"`
	DataTermino       string      `json:"data_termino"`
	DataLimite        string      `json:"data_limite"`
	SLA               string      `json:"sla"`
	IC                string      `json:"ic"`
	Localidade        string      `json:"localidade"`
	ModoAbertura      string      `json:"modo_abertura"`
	Solucao           string      `json:"solucao"`
	Sintoma           string      `json:"sintoma"`
	TotalHoras        float64     `json:"total_horas"`
	TotalAtividades   int         `json:"total_atividades"`
	Atividades        []Atividade `json:"atividades"`
}

func getChamado(numero string) (*ChamadoResult, error) {
	token, cookie, err := qualitorLogin()
	if err != nil {
		return nil, err
	}

	safe := safeSQLString(numero)

	sqlChamado := fmt.Sprintf(`SELECT
  NUMERO, TITULO, DESCRICAO, CLIENTE, SOLICITANTE, STATUS, PRIORIDADE,
  TIPO, EQUIPE, ATENDENTE, CRIADOR,
  CLASSIFICACAO_1, CLASSIFICACAO_2, CLASSIFICACAO_3,
  DATA_ABERTURA, ULTIMA_ATUALIZACAO, DATA_INICIO, DATA_TERMINO, DATA_LIMITE,
  SLA, EXPIRADO, IC, LOCALIDADE, MODO_ABERTURA, SOLUCAO, SINTOMA
FROM lgit_chamados
WHERE NUMERO = '%s'`, safe)

	sqlAtividades := fmt.Sprintf(`SELECT
  a.dsacompanhamento,
  a.dtacompanhamento,
  a.nrduracao,
  a.dtinicioacompanhamento,
  a.dtterminoacompanhamento,
  u.nmusuario AS responsavel
FROM QUALITOR.dbo.hd_acompanhamento a
LEFT JOIN QUALITOR.dbo.ad_usuario u ON u.cdusuario = a.cdusuario
WHERE a.cdchamado = '%s'
ORDER BY a.dtacompanhamento DESC`, safe)

	xmlChamado, err := qualitorQuery(token, cookie, sqlChamado)
	if err != nil {
		return nil, err
	}

	xmlAtividades, err := qualitorQuery(token, cookie, sqlAtividades)
	if err != nil {
		return nil, err
	}

	// Parse chamado
	decoded := extractResult(xmlChamado)
	items := extractDataItems(decoded)
	if len(items) == 0 {
		return nil, fmt.Errorf("chamado %s não encontrado", numero)
	}

	f := extractAllFields(items[0])

	classificacao := strings.Join(filterEmpty([]string{
		f["classificacao_1"], f["classificacao_2"], f["classificacao_3"],
	}), " > ")

	result := &ChamadoResult{
		Numero:            f["numero"],
		Titulo:            f["titulo"],
		Descricao:         f["descricao"],
		Cliente:           f["cliente"],
		Solicitante:       f["solicitante"],
		Status:            f["status"],
		Prioridade:        f["prioridade"],
		Tipo:              f["tipo"],
		Equipe:            f["equipe"],
		Atendente:         f["atendente"],
		Criador:           f["criador"],
		Classificacao:     classificacao,
		DataAbertura:      formatDateBR(f["data_abertura"]),
		UltimaAtualizacao: formatDateBR(f["ultima_atualizacao"]),
		DataTermino:       formatDateBR(f["data_termino"]),
		DataLimite:        formatDateBR(f["data_limite"]),
		SLA:               f["sla"],
		IC:                f["ic"],
		Localidade:        f["localidade"],
		ModoAbertura:      f["modo_abertura"],
		Solucao:           f["solucao"],
		Sintoma:           f["sintoma"],
	}

	// Parse atividades
	decodedAtiv := extractResult(xmlAtividades)
	ativItems := extractDataItems(decodedAtiv)
	var totalMinutos float64

	for _, block := range ativItems {
		responsavel := extractField(block, "responsavel")
		descricao := extractField(block, "dsacompanhamento")
		dtAcomp := extractField(block, "dtacompanhamento")
		duracaoStr := extractField(block, "nrduracao")
		inicio := extractField(block, "dtinicioacompanhamento")
		fim := extractField(block, "dtterminoacompanhamento")

		duracao := parseFloat(duracaoStr)
		totalMinutos += duracao * 60

		dataRef := inicio
		if dataRef == "" {
			dataRef = dtAcomp
		}

		horaInicio := "-"
		horaFim := "-"
		if len(inicio) >= 16 {
			horaInicio = inicio[11:16]
		}
		if len(fim) >= 16 {
			horaFim = fim[11:16]
		}

		tempo := "-"
		if duracao > 0 {
			tempo = fmt.Sprintf("%.1fh", duracao)
		}

		result.Atividades = append(result.Atividades, Atividade{
			Responsavel: responsavel,
			Descricao:   descricao,
			Data:        formatDateBR(dataRef),
			HoraInicio:  horaInicio,
			HoraFim:     horaFim,
			Tempo:       tempo,
		})
	}

	result.TotalHoras = float64(int(totalMinutos/60*10)) / 10
	result.TotalAtividades = len(result.Atividades)

	return result, nil
}

// ── Tool: pesquisar_chamados ──

type PesquisaChamadosArgs struct {
	Cliente   string `json:"cliente"`
	Fila      string `json:"fila"`
	Atendente string `json:"atendente"`
	Tipo      string `json:"tipo"`
	Categoria string `json:"categoria"`
	Dias      int    `json:"dias"`
	Status    string `json:"status"`
	Busca     string `json:"busca"`
}

type ChamadoResumo struct {
	Numero       string `json:"numero"`
	Titulo       string `json:"titulo"`
	Cliente      string `json:"cliente"`
	Status       string `json:"status"`
	Prioridade   string `json:"prioridade"`
	Tipo         string `json:"tipo"`
	Equipe       string `json:"equipe"`
	Atendente    string `json:"atendente"`
	Categoria    string `json:"categoria"`
	DataAbertura string `json:"data_abertura"`
	IdadeDias    string `json:"idade_dias"`
}

func pesquisarChamados(args *PesquisaChamadosArgs) ([]ChamadoResumo, error) {
	token, cookie, err := qualitorLogin()
	if err != nil {
		return nil, err
	}

	// Defaults
	if args.Dias == 0 {
		args.Dias = 7
	}

	sql := fmt.Sprintf(`SELECT TOP 200
  NUMERO,
  DATEDIFF(DAY, DATA_ABERTURA, GETDATE()) AS AGE_DIAS,
  TITULO,
  CLIENTE,
  STATUS,
  PRIORIDADE,
  DATA_ABERTURA,
  CASE
    WHEN CLASSIFICACAO_1 LIKE '1%%' OR CLASSIFICACAO_1 LIKE '2%%'
      OR CLASSIFICACAO_1 LIKE '3%%' OR CLASSIFICACAO_1 LIKE '4%%'
      OR CLASSIFICACAO_1 LIKE '5%%' OR CLASSIFICACAO_1 LIKE '8%%'
      OR CLASSIFICACAO_1 LIKE '9%%'
    THEN SUBSTRING(CLASSIFICACAO_1, CHARINDEX(' ', CLASSIFICACAO_1) + 1, LEN(CLASSIFICACAO_1))
    ELSE CLASSIFICACAO_1
  END AS CATEGORIA,
  TIPO,
  ATENDENTE,
  EQUIPE
FROM lgit_chamados
WHERE DATA_ABERTURA >= DATEADD(DAY, -%d, GETDATE())`, args.Dias)

	if args.Cliente != "" {
		parts := strings.Split(args.Cliente, ",")
		quoted := make([]string, len(parts))
		for i, p := range parts {
			quoted[i] = "'" + safeSQLString(strings.TrimSpace(p)) + "'"
		}
		sql += "\n  AND CLIENTE IN (" + strings.Join(quoted, ",") + ")"
	}

	if args.Fila != "" {
		parts := strings.Split(args.Fila, ",")
		quoted := make([]string, len(parts))
		for i, p := range parts {
			quoted[i] = "'" + safeSQLString(strings.TrimSpace(p)) + "'"
		}
		sql += "\n  AND EQUIPE IN (" + strings.Join(quoted, ",") + ")"
	}

	if args.Atendente != "" {
		parts := strings.Split(args.Atendente, ",")
		quoted := make([]string, len(parts))
		for i, p := range parts {
			quoted[i] = "'" + safeSQLString(strings.TrimSpace(p)) + "'"
		}
		sql += "\n  AND ATENDENTE IN (" + strings.Join(quoted, ",") + ")"
	}

	if args.Tipo != "" {
		parts := strings.Split(args.Tipo, ",")
		quoted := make([]string, len(parts))
		for i, p := range parts {
			quoted[i] = "'" + safeSQLString(strings.TrimSpace(p)) + "'"
		}
		sql += "\n  AND TIPO IN (" + strings.Join(quoted, ",") + ")"
	}

	if args.Categoria != "" {
		parts := strings.Split(args.Categoria, ",")
		conditions := make([]string, len(parts))
		for i, p := range parts {
			conditions[i] = "CLASSIFICACAO_1 LIKE '%" + safeSQLString(strings.TrimSpace(p)) + "%'"
		}
		sql += "\n  AND (" + strings.Join(conditions, " OR ") + ")"
	}

	if args.Status == "" {
		// Default: exclui Cancelado e Terminado
		sql += "\n  AND STATUS NOT IN ('Cancelado','TERMINADO')"
	} else {
		parts := strings.Split(args.Status, ",")
		quoted := make([]string, len(parts))
		for i, p := range parts {
			quoted[i] = "'" + safeSQLString(strings.TrimSpace(p)) + "'"
		}
		sql += "\n  AND STATUS IN (" + strings.Join(quoted, ",") + ")"
	}

	if args.Busca != "" {
		safe := safeSQLString(args.Busca)
		like := "%" + safe + "%"
		sql += "\n  AND (TITULO LIKE '" + like + "' OR DESCRICAO LIKE '" + like + "' OR ID IN (SELECT cdchamado FROM QUALITOR.dbo.hd_acompanhamento WHERE dsacompanhamento LIKE '" + like + "'))"
	}

	sql += "\nORDER BY DATA_ABERTURA DESC"

	xmlResult, err := qualitorQuery(token, cookie, sql)
	if err != nil {
		return nil, err
	}

	decoded := extractResult(xmlResult)
	items := extractDataItems(decoded)

	var chamados []ChamadoResumo
	for _, block := range items {
		f := extractAllFields(block)
		chamados = append(chamados, ChamadoResumo{
			Numero:       f["numero"],
			Titulo:       f["titulo"],
			Cliente:      f["cliente"],
			Status:       f["status"],
			Prioridade:   f["prioridade"],
			Tipo:         f["tipo"],
			Equipe:       f["equipe"],
			Atendente:    f["atendente"],
			Categoria:    f["categoria"],
			DataAbertura: formatDateBR(f["data_abertura"]),
			IdadeDias:    f["age_dias"],
		})
	}

	return chamados, nil
}

func formatPesquisaForLLM(chamados []ChamadoResumo, args PesquisaChamadosArgs) string {
	var b strings.Builder

	b.WriteString(fmt.Sprintf("# Pesquisa de Chamados (%d resultados)\n\n", len(chamados)))

	// Filtros aplicados
	b.WriteString("**Filtros:** ")
	filtros := []string{fmt.Sprintf("Período: %d dias", args.Dias)}
	if args.Cliente != "" {
		filtros = append(filtros, "Cliente: "+args.Cliente)
	}
	if args.Fila != "" {
		filtros = append(filtros, "Fila: "+args.Fila)
	}
	if args.Atendente != "" {
		filtros = append(filtros, "Atendente: "+args.Atendente)
	}
	if args.Tipo != "" {
		filtros = append(filtros, "Tipo: "+args.Tipo)
	}
	if args.Categoria != "" {
		filtros = append(filtros, "Categoria: "+args.Categoria)
	}
	if args.Status != "" {
		filtros = append(filtros, "Status: "+args.Status)
	}
	if args.Busca != "" {
		filtros = append(filtros, "Busca: \""+args.Busca+"\"")
	}
	b.WriteString(strings.Join(filtros, " | ") + "\n\n")

	if len(chamados) == 0 {
		b.WriteString("Nenhum chamado encontrado com os filtros informados.\n")
		return b.String()
	}

	// Resumo por status
	statusCount := make(map[string]int)
	for _, c := range chamados {
		statusCount[c.Status]++
	}
	b.WriteString("**Resumo por status:** ")
	var statusParts []string
	for s, count := range statusCount {
		statusParts = append(statusParts, fmt.Sprintf("%s (%d)", s, count))
	}
	b.WriteString(strings.Join(statusParts, ", ") + "\n\n")

	// Tabela
	b.WriteString("| # | Número | Título | Cliente | Status | Prioridade | Atendente | Abertura | Idade |\n")
	b.WriteString("|---|--------|--------|---------|--------|------------|-----------|----------|-------|\n")

	for i, c := range chamados {
		titulo := c.Titulo
		if len(titulo) > 60 {
			titulo = titulo[:57] + "..."
		}
		b.WriteString(fmt.Sprintf("| %d | %s | %s | %s | %s | %s | %s | %s | %s dias |\n",
			i+1, c.Numero, titulo, c.Cliente, c.Status, c.Prioridade, c.Atendente, c.DataAbertura, c.IdadeDias))
	}

	return b.String()
}

// ── Tool: relatorio_chargeability ──

var equipeMembros = map[string][]string{
	"CLOUD":         {"Camila Silva Castelo Branco", "Cassiano Ficagna Barbosa", "Gustavo Oliveira Ramos de Lima", "Murilo Abineder Julieti", "Pedro Miguel Dias Oliveira", "Rafael Broglia Szyszko", "Rafael Matheus Ramos Galiani", "Reginaldo Silva Rocha", "Vinicius Santos Ferreira"},
	"LINUX":         {"Carlos Roitman Amaral Maceno", "Douglas Michel da Silva Costa", "Edilson Pereira Caldas", "Eliel Borba Vaz", "Fabio Aparecido dos Santos", "João Paulo Santos Araujo", "Julyana Silva da Rocha", "Odair Batista Gonçalves dos Santos", "Vinicius Santos Silva"},
	"REDES":         {"Abner Mateus Alves Silva", "Fabiano Francisco Martins", "Kevin Ramos Santos", "Lucas Camargo Reichert", "Odilon Pascoal Mendes Silva"},
	"VIRTUALIZAÇÃO": {"Pedro Oliveira De Aguiar", "Rafael Duarte Fontenele Liberato"},
	"WINDOWS":       {"André Luiz Navarro Martos", "Guilherme Ferreira Rocha", "José Luciano Silva Cavalcante", "Julio Cezar Vidal de Oliveira", "Lucas Gabriel De Souza Calado", "Rafael Ferreira dos Santos"},
}

type ChargeabilityArgs struct {
	Equipe    string `json:"equipe"`
	Atendente string `json:"atendente"`
	Fila      string `json:"fila"`
	Dias      int    `json:"dias"`
}

type ProfissionalChargeability struct {
	Nome         string
	Registros    int
	HorasNormais float64
	HorasExtra   float64
	Total        float64
	HorasUteis   float64
	Chargeability float64
}

func calcularHorasUteis(dias int) float64 {
	now := time.Now()
	var horas float64
	for i := 0; i < dias; i++ {
		d := now.AddDate(0, 0, -i)
		dow := d.Weekday()
		if dow >= time.Monday && dow <= time.Friday {
			horas += 8
		}
	}
	return horas
}

func calcNormalExtra(inicio, fim string, totalMin float64) (normal, extra float64) {
	if inicio == "" || fim == "" {
		return totalMin, 0
	}
	dtInicio, err1 := time.Parse("2006-01-02 15:04:05.000", inicio)
	dtFim, err2 := time.Parse("2006-01-02 15:04:05.000", fim)
	if err1 != nil || err2 != nil || !dtFim.After(dtInicio) {
		// Try shorter format
		dtInicio, err1 = time.Parse("2006-01-02 15:04:05", inicio)
		dtFim, err2 = time.Parse("2006-01-02 15:04:05", fim)
		if err1 != nil || err2 != nil || !dtFim.After(dtInicio) {
			return totalMin, 0
		}
	}

	diffMin := dtFim.Sub(dtInicio).Minutes()
	if diffMin <= 0 {
		return 0, totalMin
	}

	var minutosNormais float64
	cursor := dtInicio
	for cursor.Before(dtFim) {
		dow := cursor.Weekday()
		if dow >= time.Monday && dow <= time.Friday {
			diaBase := time.Date(cursor.Year(), cursor.Month(), cursor.Day(), 0, 0, 0, 0, cursor.Location())
			ns := diaBase.Add(9 * time.Hour)
			ne := diaBase.Add(18 * time.Hour)
			minutosNormais += overlapMin(cursor, dtFim, ns, ne)
		}
		next := time.Date(cursor.Year(), cursor.Month(), cursor.Day()+1, 0, 0, 0, 0, cursor.Location())
		if !next.After(cursor) {
			break
		}
		cursor = next
	}

	prop := minutosNormais / diffMin
	normal = math.Round(totalMin*prop*10) / 10
	extra = math.Round((totalMin-normal)*10) / 10
	return
}

func overlapMin(a1, a2, b1, b2 time.Time) float64 {
	start := a1
	if b1.After(start) {
		start = b1
	}
	end := a2
	if b2.Before(end) {
		end = b2
	}
	diff := end.Sub(start).Minutes()
	if diff > 0 {
		return diff
	}
	return 0
}

func relatorioChargeability(args *ChargeabilityArgs) ([]ProfissionalChargeability, error) {
	token, cookie, err := qualitorLogin()
	if err != nil {
		return nil, err
	}

	if args.Dias == 0 {
		args.Dias = 30
	}

	// Resolve atendentes
	var atendentes []string
	if args.Atendente != "" {
		atendentes = strings.Split(args.Atendente, ",")
		for i := range atendentes {
			atendentes[i] = strings.TrimSpace(atendentes[i])
		}
	} else if args.Equipe != "" {
		for _, eq := range strings.Split(args.Equipe, ",") {
			eq = strings.TrimSpace(eq)
			if membros, ok := equipeMembros[eq]; ok {
				atendentes = append(atendentes, membros...)
			}
		}
	}

	sql := `SELECT
  RESPONSAVEL,
  DATEPART(DW, COALESCE(NULLIF(DATA_INICIO_ACOMPANHAMENTO, ''), DATA_COMPLETA)) AS DW,
  DATEPART(HOUR, COALESCE(NULLIF(DATA_INICIO_ACOMPANHAMENTO, ''), DATA_COMPLETA)) AS HH,
  DATA_INICIO_ACOMPANHAMENTO AS DT_INICIO,
  DATA_FIM_ACOMPANHAMENTO AS DT_FIM,
  CAST(LEFT(Total_FUNC, 2) AS INT) * 60
    + CAST(SUBSTRING(Total_FUNC, 4, 2) AS INT)
    + CAST(RIGHT(Total_FUNC, 2) AS FLOAT) / 60 AS MINUTOS
FROM QUALITOR.dbo.vw_LGIT_HorasPorChamado
WHERE Total_FUNC IS NOT NULL AND Total_FUNC != ''
  AND COALESCE(NULLIF(DATA_INICIO_ACOMPANHAMENTO, ''), DATA_COMPLETA) >= DATEADD(DAY, -` + fmt.Sprintf("%d", args.Dias) + `, GETDATE())`

	if args.Fila != "" {
		parts := strings.Split(args.Fila, ",")
		quoted := make([]string, len(parts))
		for i, p := range parts {
			quoted[i] = "'" + safeSQLString(strings.TrimSpace(p)) + "'"
		}
		sql += "\n  AND EQUIPE IN (" + strings.Join(quoted, ",") + ")"
	}

	if len(atendentes) > 0 {
		quoted := make([]string, len(atendentes))
		for i, a := range atendentes {
			quoted[i] = "'" + safeSQLString(a) + "'"
		}
		sql += "\n  AND RESPONSAVEL IN (" + strings.Join(quoted, ",") + ")"
	}

	xmlResult, err := qualitorQuery(token, cookie, sql)
	if err != nil {
		return nil, err
	}

	decoded := extractResult(xmlResult)
	items := extractDataItems(decoded)

	profMap := make(map[string]*struct {
		registros int
		normais   float64
		extra     float64
	})

	for _, block := range items {
		nome := extractField(block, "responsavel")
		if nome == "" {
			continue
		}
		dtInicio := extractField(block, "dt_inicio")
		dtFim := extractField(block, "dt_fim")
		minutos := parseFloat(extractField(block, "minutos"))

		if _, ok := profMap[nome]; !ok {
			profMap[nome] = &struct {
				registros int
				normais   float64
				extra     float64
			}{}
		}
		p := profMap[nome]
		p.registros++

		normal, extra := calcNormalExtra(dtInicio, dtFim, minutos)
		p.normais += normal
		p.extra += extra
	}

	horasUteis := calcularHorasUteis(args.Dias)
	var result []ProfissionalChargeability

	for nome, data := range profMap {
		total := (data.normais + data.extra) / 60
		hn := math.Round(data.normais/60*10) / 10
		he := math.Round(data.extra/60*10) / 10
		totalH := math.Round(total*10) / 10
		charge := 0.0
		if horasUteis > 0 {
			charge = math.Round(total/horasUteis*1000) / 10
		}
		result = append(result, ProfissionalChargeability{
			Nome:          nome,
			Registros:     data.registros,
			HorasNormais:  hn,
			HorasExtra:    he,
			Total:         totalH,
			HorasUteis:    horasUteis,
			Chargeability: charge,
		})
	}

	// Sort by total desc
	for i := 0; i < len(result); i++ {
		for j := i + 1; j < len(result); j++ {
			if result[j].Total > result[i].Total {
				result[i], result[j] = result[j], result[i]
			}
		}
	}

	return result, nil
}

func formatChargeabilityForLLM(result []ProfissionalChargeability, args ChargeabilityArgs) string {
	var b strings.Builder

	b.WriteString("# Relatório de Chargeability\n\n")

	filtros := []string{fmt.Sprintf("Período: %d dias", args.Dias)}
	if args.Equipe != "" {
		filtros = append(filtros, "Equipe: "+args.Equipe)
	}
	if args.Atendente != "" {
		filtros = append(filtros, "Atendente: "+args.Atendente)
	}
	if args.Fila != "" {
		filtros = append(filtros, "Fila: "+args.Fila)
	}
	horasUteis := 0.0
	if len(result) > 0 {
		horasUteis = result[0].HorasUteis
	}
	filtros = append(filtros, fmt.Sprintf("Base: %.0fh úteis (8h/dia, seg-sex)", horasUteis))
	b.WriteString("**Filtros:** " + strings.Join(filtros, " | ") + "\n\n")

	if len(result) == 0 {
		b.WriteString("Nenhum dado encontrado.\n")
		return b.String()
	}

	// Totais
	var totalHN, totalHE, totalH float64
	var totalReg int
	for _, r := range result {
		totalHN += r.HorasNormais
		totalHE += r.HorasExtra
		totalH += r.Total
		totalReg += r.Registros
	}
	b.WriteString(fmt.Sprintf("**Totais:** %d registros | %.1fh normais | %.1fh extra | **%.1fh total**\n\n", totalReg, totalHN, totalHE, totalH))

	// Tabela
	b.WriteString("| Profissional | Registros | Horas Normais | Horas Extra | Total | Chargeability |\n")
	b.WriteString("|---|---|---|---|---|---|\n")

	for _, r := range result {
		chargeIcon := "🔴"
		if r.Chargeability >= 100 {
			chargeIcon = "🔵"
		} else if r.Chargeability >= 80 {
			chargeIcon = "🟢"
		} else if r.Chargeability >= 50 {
			chargeIcon = "🟡"
		}
		b.WriteString(fmt.Sprintf("| %s | %d | %.1fh | %.1fh | **%.1fh** | %s %.1f%% |\n",
			r.Nome, r.Registros, r.HorasNormais, r.HorasExtra, r.Total, chargeIcon, r.Chargeability))
	}

	return b.String()
}

// ── Tool: pesquisar_usuarios / get_usuario ──

type UsuarioResumo struct {
	Nome       string `json:"nome"`
	Email      string `json:"email"`
	Cargo      string `json:"cargo"`
	Ativo      string `json:"ativo"`
	Disponivel string `json:"disponivel"`
}

func pesquisarUsuarios(busca string) ([]UsuarioResumo, error) {
	token, cookie, err := qualitorLogin()
	if err != nil {
		return nil, err
	}

	safe := safeSQLString(busca)
	like := "%" + safe + "%"

	sql := `SELECT u.nmusuario, u.dsemail, u.idativo, u.iddisponivel, c.nmcargo
FROM QUALITOR.dbo.ad_usuario u
LEFT JOIN QUALITOR.dbo.ad_cargo c ON c.cdcargo = u.cdcargo
WHERE (u.nmusuario LIKE '` + like + `' OR u.dsemail LIKE '` + like + `' OR u.nmusuariorede LIKE '` + like + `')
  AND u.idativo = 'Y'
ORDER BY u.nmusuario`

	xmlResult, err := qualitorQuery(token, cookie, sql)
	if err != nil {
		return nil, err
	}

	decoded := extractResult(xmlResult)
	items := extractDataItems(decoded)
	var usuarios []UsuarioResumo
	for _, block := range items {
		f := extractAllFields(block)
		usuarios = append(usuarios, UsuarioResumo{
			Nome:       f["nmusuario"],
			Email:      f["dsemail"],
			Cargo:      f["nmcargo"],
			Ativo:      f["idativo"],
			Disponivel: f["iddisponivel"],
		})
	}
	return usuarios, nil
}

type UsuarioDetalhado struct {
	Nome         string   `json:"nome"`
	Email        string   `json:"email"`
	Login        string   `json:"login"`
	Cargo        string   `json:"cargo"`
	Superior     string   `json:"superior"`
	Ativo        string   `json:"ativo"`
	Disponivel   string   `json:"disponivel"`
	Bloqueado    string   `json:"bloqueado"`
	Equipes      []string `json:"equipes"`
	Total30d     string   `json:"total_chamados_30d"`
	Terminados   string   `json:"terminados_30d"`
	Abertos      string   `json:"abertos_30d"`
}

func getUsuario(nome string) (*UsuarioDetalhado, error) {
	token, cookie, err := qualitorLogin()
	if err != nil {
		return nil, err
	}

	safe := safeSQLString(nome)

	// Dados do usuario
	sqlUser := `SELECT u.nmusuario, u.dsemail, u.nmusuariorede, u.idativo, u.iddisponivel, u.idbloqueado,
  c.nmcargo, s.nmusuario AS superior
FROM QUALITOR.dbo.ad_usuario u
LEFT JOIN QUALITOR.dbo.ad_cargo c ON c.cdcargo = u.cdcargo
LEFT JOIN QUALITOR.dbo.ad_usuario s ON s.cdusuario = u.cdusuariosuperior
WHERE u.nmusuario = '` + safe + `'`

	// Equipes
	sqlEquipes := `SELECT e.nmequipe
FROM QUALITOR.dbo.hd_equipe e
JOIN QUALITOR.dbo.hd_equipeusuario eu ON eu.cdequipe = e.cdequipe
JOIN QUALITOR.dbo.ad_usuario u ON u.cdusuario = eu.cdusuario
WHERE u.nmusuario = '` + safe + `'
ORDER BY e.nmequipe`

	// Stats 30 dias
	sqlStats := `SELECT
  COUNT(*) AS total,
  SUM(CASE WHEN STATUS = 'TERMINADO' THEN 1 ELSE 0 END) AS terminados,
  SUM(CASE WHEN STATUS NOT IN ('TERMINADO','Cancelado') THEN 1 ELSE 0 END) AS abertos
FROM lgit_chamados
WHERE ATENDENTE = '` + safe + `' AND DATA_ABERTURA >= DATEADD(DAY, -30, GETDATE())`

	xmlUser, err := qualitorQuery(token, cookie, sqlUser)
	if err != nil {
		return nil, err
	}
	xmlEquipes, err := qualitorQuery(token, cookie, sqlEquipes)
	if err != nil {
		return nil, err
	}
	xmlStats, err := qualitorQuery(token, cookie, sqlStats)
	if err != nil {
		return nil, err
	}

	// Parse user
	decoded := extractResult(xmlUser)
	items := extractDataItems(decoded)
	if len(items) == 0 {
		return nil, fmt.Errorf("usuário '%s' não encontrado", nome)
	}
	f := extractAllFields(items[0])

	result := &UsuarioDetalhado{
		Nome:       f["nmusuario"],
		Email:      f["dsemail"],
		Login:      f["nmusuariorede"],
		Cargo:      f["nmcargo"],
		Superior:   f["superior"],
		Ativo:      f["idativo"],
		Disponivel: f["iddisponivel"],
		Bloqueado:  f["idbloqueado"],
	}

	// Parse equipes
	decodedEq := extractResult(xmlEquipes)
	eqItems := extractDataItems(decodedEq)
	for _, block := range eqItems {
		eq := extractField(block, "nmequipe")
		if eq != "" {
			result.Equipes = append(result.Equipes, eq)
		}
	}

	// Parse stats
	decodedSt := extractResult(xmlStats)
	stItems := extractDataItems(decodedSt)
	if len(stItems) > 0 {
		sf := extractAllFields(stItems[0])
		result.Total30d = sf["total"]
		result.Terminados = sf["terminados"]
		result.Abertos = sf["abertos"]
	}

	return result, nil
}

func formatUsuarioForLLM(u *UsuarioDetalhado) string {
	var b strings.Builder
	b.WriteString(fmt.Sprintf("# Usuário: %s\n\n", u.Nome))
	b.WriteString(fmt.Sprintf("**E-mail:** %s\n", u.Email))
	b.WriteString(fmt.Sprintf("**Login:** %s\n", u.Login))
	b.WriteString(fmt.Sprintf("**Cargo:** %s\n", u.Cargo))
	b.WriteString(fmt.Sprintf("**Superior:** %s\n", u.Superior))

	status := "Ativo"
	if u.Ativo != "Y" {
		status = "Inativo"
	}
	if u.Bloqueado == "S" {
		status += " (Bloqueado)"
	}
	b.WriteString(fmt.Sprintf("**Status:** %s | **Disponível:** %s\n", status, u.Disponivel))

	if len(u.Equipes) > 0 {
		b.WriteString(fmt.Sprintf("**Equipes:** %s\n", strings.Join(u.Equipes, ", ")))
	}

	b.WriteString(fmt.Sprintf("\n## Chamados (últimos 30 dias)\n"))
	b.WriteString(fmt.Sprintf("- Total: %s\n", u.Total30d))
	b.WriteString(fmt.Sprintf("- Terminados: %s\n", u.Terminados))
	b.WriteString(fmt.Sprintf("- Em aberto: %s\n", u.Abertos))

	return b.String()
}

func formatPesquisaUsuariosForLLM(usuarios []UsuarioResumo, busca string) string {
	var b strings.Builder
	b.WriteString(fmt.Sprintf("# Pesquisa de Usuários (%d resultados)\n\n", len(usuarios)))
	b.WriteString(fmt.Sprintf("**Busca:** \"%s\"\n\n", busca))

	if len(usuarios) == 0 {
		b.WriteString("Nenhum usuário encontrado.\n")
		return b.String()
	}

	b.WriteString("| Nome | E-mail | Cargo | Disponível |\n")
	b.WriteString("|------|--------|-------|------------|\n")
	for _, u := range usuarios {
		disp := "Sim"
		if u.Disponivel != "Y" {
			disp = "Não"
		}
		b.WriteString(fmt.Sprintf("| %s | %s | %s | %s |\n", u.Nome, u.Email, u.Cargo, disp))
	}
	return b.String()
}

func filterEmpty(ss []string) []string {
	var out []string
	for _, s := range ss {
		if s != "" {
			out = append(out, s)
		}
	}
	return out
}

func parseFloat(s string) float64 {
	var f float64
	fmt.Sscanf(s, "%f", &f)
	return f
}

// ── MCP SSE Protocol (JSON-RPC over stdio) ──

type JSONRPCRequest struct {
	JSONRPC string          `json:"jsonrpc"`
	ID      json.RawMessage `json:"id,omitempty"`
	Method  string          `json:"method"`
	Params  json.RawMessage `json:"params,omitempty"`
}

type JSONRPCResponse struct {
	JSONRPC string          `json:"jsonrpc"`
	ID      json.RawMessage `json:"id,omitempty"`
	Result  interface{}     `json:"result,omitempty"`
	Error   *RPCError       `json:"error,omitempty"`
}

type RPCError struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
}

func sendResponse(w io.Writer, id json.RawMessage, result interface{}) {
	resp := JSONRPCResponse{JSONRPC: "2.0", ID: id, Result: result}
	data, _ := json.Marshal(resp)
	fmt.Fprintf(w, "%s\n", data)
}

func sendError(w io.Writer, id json.RawMessage, code int, msg string) {
	resp := JSONRPCResponse{JSONRPC: "2.0", ID: id, Error: &RPCError{Code: code, Message: msg}}
	data, _ := json.Marshal(resp)
	fmt.Fprintf(w, "%s\n", data)
}

func handleInitialize(id json.RawMessage, w io.Writer) {
	sendResponse(w, id, map[string]interface{}{
		"protocolVersion": "2024-11-05",
		"capabilities": map[string]interface{}{
			"tools": map[string]interface{}{},
		},
		"serverInfo": map[string]interface{}{
			"name":    "qualitor-mcp",
			"version": "1.0.0",
		},
	})
}

func handleToolsList(id json.RawMessage, w io.Writer) {
	sendResponse(w, id, map[string]interface{}{
		"tools": []map[string]interface{}{
			{
				"name":        "get_chamado",
				"description": "Busca informações detalhadas de um chamado no Qualitor pelo número. Retorna dados do chamado (título, cliente, status, prioridade, equipe, atendente, SLA, datas, classificação, descrição, solução) e todas as atividades/acompanhamentos registrados com responsável, data, horários e descrição do que foi feito. Inclui o total de horas de esforço.",
				"inputSchema": map[string]interface{}{
					"type": "object",
					"properties": map[string]interface{}{
						"numero": map[string]interface{}{
							"type":        "string",
							"description": "Número do chamado no Qualitor (ex: 1081120)",
						},
					},
					"required": []string{"numero"},
				},
			},
			{
				"name":        "pesquisar_chamados",
				"description": "Pesquisa chamados no Qualitor com filtros opcionais. Retorna lista de chamados com número, título, cliente, status, prioridade, tipo, equipe, atendente, data de abertura e idade em dias. Todos os filtros são opcionais — se não informados, usam valores padrão (todas as filas, todos os clientes, todos os atendentes, últimos 7 dias).",
				"inputSchema": map[string]interface{}{
					"type": "object",
					"properties": map[string]interface{}{
						"cliente": map[string]interface{}{
							"type":        "string",
							"description": "Nome do cliente para filtrar. Pode ser múltiplos separados por vírgula (ex: 'HINODE' ou 'HINODE,LEO MADEIRAS'). Padrão: todos os clientes.",
						},
						"fila": map[string]interface{}{
							"type":        "string",
							"description": "Fila/equipe do Qualitor (ex: 'ADVANCED - REDES', 'ADVANCED - CLOUD'). Múltiplos separados por vírgula. Padrão: todas as filas.",
						},
						"atendente": map[string]interface{}{
							"type":        "string",
							"description": "Nome do atendente para filtrar (ex: 'Odilon Pascoal Mendes Silva'). Múltiplos separados por vírgula. Padrão: todos os atendentes.",
						},
						"tipo": map[string]interface{}{
							"type":        "string",
							"description": "Tipo do chamado (ex: '5. Solicitação', 'INCIDENTE', 'Projeto', 'REQ - Cliente'). Múltiplos separados por vírgula. Padrão: todos os tipos.",
						},
						"categoria": map[string]interface{}{
							"type":        "string",
							"description": "Categoria/classificação do chamado (ex: 'INC - Network', 'Rede WAN/LAN', 'Banco de Dados'). Múltiplos separados por vírgula. Padrão: todas as categorias.",
						},
						"status": map[string]interface{}{
							"type":        "string",
							"description": "Status do chamado (ex: 'Em atendimento', 'Aguardando atendimento', 'TERMINADO', 'Suspenso'). Múltiplos separados por vírgula. Padrão: todos os status.",
						},
						"dias": map[string]interface{}{
							"type":        "integer",
							"description": "Quantidade de dias para buscar a partir de hoje (ex: 7, 30, 90, 120). Padrão: 7 dias.",
						},
						"busca": map[string]interface{}{
							"type":        "string",
							"description": "Texto livre para buscar no título, descrição e atividades/acompanhamentos do chamado (ex: '10.101.14.25', 'VPN', 'backup falhou'). Se o texto aparecer em qualquer atividade registrada, o chamado será retornado.",
						},
					},
				},
			},
			{
				"name":        "relatorio_chargeability",
				"description": "Gera relatório de chargeability (aproveitamento de horas) por profissional. Mostra horas normais (09:00-18:00 seg-sex), horas extra (fora do horário ou fins de semana), total e percentual de chargeability baseado nas horas úteis do período. Pode filtrar por equipe (CLOUD, LINUX, REDES, VIRTUALIZAÇÃO, WINDOWS), por atendente específico ou por fila do Qualitor.",
				"inputSchema": map[string]interface{}{
					"type": "object",
					"properties": map[string]interface{}{
						"equipe": map[string]interface{}{
							"type":        "string",
							"description": "Nome da equipe para filtrar (CLOUD, LINUX, REDES, VIRTUALIZAÇÃO, WINDOWS). Múltiplas separadas por vírgula. Filtra pelos membros da equipe. Padrão: todos.",
						},
						"atendente": map[string]interface{}{
							"type":        "string",
							"description": "Nome do profissional específico (ex: 'Odilon Pascoal Mendes Silva'). Múltiplos separados por vírgula. Tem prioridade sobre equipe. Padrão: todos.",
						},
						"fila": map[string]interface{}{
							"type":        "string",
							"description": "Fila do Qualitor para filtrar os apontamentos (ex: 'ADVANCED - REDES', 'ADVANCED - CLOUD'). Múltiplas separadas por vírgula. Padrão: todas.",
						},
						"dias": map[string]interface{}{
							"type":        "integer",
							"description": "Quantidade de dias para o relatório (ex: 7, 15, 30, 60, 90). Padrão: 30 dias.",
						},
					},
				},
			},
			{
				"name":        "pesquisar_usuarios",
				"description": "Pesquisa usuários ativos no Qualitor por nome, e-mail ou login de rede. Retorna nome, e-mail, cargo e disponibilidade.",
				"inputSchema": map[string]interface{}{
					"type": "object",
					"properties": map[string]interface{}{
						"busca": map[string]interface{}{
							"type":        "string",
							"description": "Texto para buscar no nome, e-mail ou login do usuário (ex: 'Odilon', 'odilon.silva', 'grupothink')",
						},
					},
					"required": []string{"busca"},
				},
			},
			{
				"name":        "get_usuario",
				"description": "Busca informações detalhadas de um usuário no Qualitor pelo nome completo. Retorna dados pessoais (e-mail, login, cargo, superior hierárquico), equipes que participa, status (ativo/bloqueado/disponível) e estatísticas de chamados dos últimos 30 dias.",
				"inputSchema": map[string]interface{}{
					"type": "object",
					"properties": map[string]interface{}{
						"nome": map[string]interface{}{
							"type":        "string",
							"description": "Nome completo do usuário no Qualitor (ex: 'Odilon Pascoal Mendes Silva')",
						},
					},
					"required": []string{"nome"},
				},
			},
		},
	})
}

func handleToolCall(id json.RawMessage, params json.RawMessage, w io.Writer) {
	var p struct {
		Name      string          `json:"name"`
		Arguments json.RawMessage `json:"arguments"`
	}
	json.Unmarshal(params, &p)

	switch p.Name {
	case "get_chamado":
		var args struct {
			Numero string `json:"numero"`
		}
		json.Unmarshal(p.Arguments, &args)

		if args.Numero == "" {
			sendResponse(w, id, map[string]interface{}{
				"content": []map[string]interface{}{
					{"type": "text", "text": "Erro: número do chamado é obrigatório"},
				},
				"isError": true,
			})
			return
		}

		result, err := getChamado(args.Numero)
		if err != nil {
			sendResponse(w, id, map[string]interface{}{
				"content": []map[string]interface{}{
					{"type": "text", "text": fmt.Sprintf("Erro ao buscar chamado: %s", err.Error())},
				},
				"isError": true,
			})
			return
		}

		// Formata saída estruturada para LLM
		text := formatChamadoForLLM(result)
		sendResponse(w, id, map[string]interface{}{
			"content": []map[string]interface{}{
				{"type": "text", "text": text},
			},
		})

	case "pesquisar_chamados":
		var args PesquisaChamadosArgs
		json.Unmarshal(p.Arguments, &args)

		chamados, err := pesquisarChamados(&args)
		if err != nil {
			sendResponse(w, id, map[string]interface{}{
				"content": []map[string]interface{}{
					{"type": "text", "text": fmt.Sprintf("Erro ao pesquisar chamados: %s", err.Error())},
				},
				"isError": true,
			})
			return
		}

		text := formatPesquisaForLLM(chamados, args)
		sendResponse(w, id, map[string]interface{}{
			"content": []map[string]interface{}{
				{"type": "text", "text": text},
			},
		})

	case "relatorio_chargeability":
		var args ChargeabilityArgs
		json.Unmarshal(p.Arguments, &args)

		result, err := relatorioChargeability(&args)
		if err != nil {
			sendResponse(w, id, map[string]interface{}{
				"content": []map[string]interface{}{
					{"type": "text", "text": fmt.Sprintf("Erro ao gerar relatório: %s", err.Error())},
				},
				"isError": true,
			})
			return
		}

		text := formatChargeabilityForLLM(result, args)
		sendResponse(w, id, map[string]interface{}{
			"content": []map[string]interface{}{
				{"type": "text", "text": text},
			},
		})

	case "pesquisar_usuarios":
		var args struct {
			Busca string `json:"busca"`
		}
		json.Unmarshal(p.Arguments, &args)

		if args.Busca == "" {
			sendResponse(w, id, map[string]interface{}{
				"content": []map[string]interface{}{
					{"type": "text", "text": "Erro: campo 'busca' é obrigatório"},
				},
				"isError": true,
			})
			return
		}

		usuarios, err := pesquisarUsuarios(args.Busca)
		if err != nil {
			sendResponse(w, id, map[string]interface{}{
				"content": []map[string]interface{}{
					{"type": "text", "text": fmt.Sprintf("Erro: %s", err.Error())},
				},
				"isError": true,
			})
			return
		}

		text := formatPesquisaUsuariosForLLM(usuarios, args.Busca)
		sendResponse(w, id, map[string]interface{}{
			"content": []map[string]interface{}{
				{"type": "text", "text": text},
			},
		})

	case "get_usuario":
		var args struct {
			Nome string `json:"nome"`
		}
		json.Unmarshal(p.Arguments, &args)

		if args.Nome == "" {
			sendResponse(w, id, map[string]interface{}{
				"content": []map[string]interface{}{
					{"type": "text", "text": "Erro: campo 'nome' é obrigatório"},
				},
				"isError": true,
			})
			return
		}

		usuario, err := getUsuario(args.Nome)
		if err != nil {
			sendResponse(w, id, map[string]interface{}{
				"content": []map[string]interface{}{
					{"type": "text", "text": fmt.Sprintf("Erro: %s", err.Error())},
				},
				"isError": true,
			})
			return
		}

		text := formatUsuarioForLLM(usuario)
		sendResponse(w, id, map[string]interface{}{
			"content": []map[string]interface{}{
				{"type": "text", "text": text},
			},
		})

	default:
		sendResponse(w, id, map[string]interface{}{
			"content": []map[string]interface{}{
				{"type": "text", "text": fmt.Sprintf("Tool '%s' não encontrada", p.Name)},
			},
			"isError": true,
		})
	}
}

func formatChamadoForLLM(c *ChamadoResult) string {
	var b strings.Builder

	b.WriteString(fmt.Sprintf("# Chamado #%s\n\n", c.Numero))
	b.WriteString(fmt.Sprintf("**Título:** %s\n", c.Titulo))
	b.WriteString(fmt.Sprintf("**Status:** %s | **Prioridade:** %s | **SLA:** %s\n", c.Status, c.Prioridade, c.SLA))
	b.WriteString(fmt.Sprintf("**Cliente:** %s | **Solicitante:** %s\n", c.Cliente, c.Solicitante))
	b.WriteString(fmt.Sprintf("**Equipe:** %s | **Atendente:** %s\n", c.Equipe, c.Atendente))
	b.WriteString(fmt.Sprintf("**Tipo:** %s | **Classificação:** %s\n", c.Tipo, c.Classificacao))
	b.WriteString(fmt.Sprintf("**Abertura:** %s | **Última Atualização:** %s\n", c.DataAbertura, c.UltimaAtualizacao))
	b.WriteString(fmt.Sprintf("**Data Limite:** %s | **Data Término:** %s\n", c.DataLimite, c.DataTermino))
	b.WriteString(fmt.Sprintf("**IC:** %s | **Localidade:** %s | **Modo Abertura:** %s\n", c.IC, c.Localidade, c.ModoAbertura))
	b.WriteString(fmt.Sprintf("**Esforço Total:** %.1fh (%d atividades)\n", c.TotalHoras, c.TotalAtividades))

	if c.Descricao != "" {
		b.WriteString(fmt.Sprintf("\n## Descrição\n%s\n", c.Descricao))
	}

	if c.Sintoma != "" && c.Sintoma != c.Descricao {
		b.WriteString(fmt.Sprintf("\n## Sintoma\n%s\n", c.Sintoma))
	}

	if c.Solucao != "" {
		b.WriteString(fmt.Sprintf("\n## Solução\n%s\n", c.Solucao))
	}

	if len(c.Atividades) > 0 {
		b.WriteString("\n## Atividades / Acompanhamentos\n\n")
		for i, a := range c.Atividades {
			horario := ""
			if a.HoraInicio != "-" {
				horario = fmt.Sprintf(" (%s - %s)", a.HoraInicio, a.HoraFim)
			}
			tempo := ""
			if a.Tempo != "-" {
				tempo = fmt.Sprintf(" [%s]", a.Tempo)
			}
			b.WriteString(fmt.Sprintf("### %d. %s — %s%s%s\n", i+1, a.Data, a.Responsavel, horario, tempo))
			if a.Descricao != "" {
				b.WriteString(a.Descricao + "\n")
			}
			b.WriteString("\n")
		}
	}

	return b.String()
}

// ── Main ──

func main() {
	reader := bufio.NewReader(os.Stdin)
	writer := os.Stdout

	for {
		line, err := reader.ReadBytes('\n')
		if err != nil {
			break
		}

		line = []byte(strings.TrimSpace(string(line)))
		if len(line) == 0 {
			continue
		}

		var req JSONRPCRequest
		if err := json.Unmarshal(line, &req); err != nil {
			continue
		}

		switch req.Method {
		case "initialize":
			handleInitialize(req.ID, writer)
		case "notifications/initialized":
			// no response needed
		case "tools/list":
			handleToolsList(req.ID, writer)
		case "tools/call":
			handleToolCall(req.ID, req.Params, writer)
		case "ping":
			sendResponse(writer, req.ID, map[string]interface{}{})
		default:
			if req.ID != nil {
				sendError(writer, req.ID, -32601, fmt.Sprintf("Method not found: %s", req.Method))
			}
		}
	}
}
