package agent

import (
	"context"
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/pulse-lets-go/internal/esl"
)

// FSCollector собирает метрики с FreeSWITCH через ESL.
type FSCollector struct {
	client *esl.Client
}

// NewFSCollector создаёт FreeSWITCH-коллектор.
func NewFSCollector(host string, port int, password string) *FSCollector {
	return &FSCollector{
		client: esl.NewClient(host, port, password),
	}
}

func (fc *FSCollector) Type() string { return "freeswitch" }

func (fc *FSCollector) Connect() error {
	// ESL client подключается синхронно через tryConnect
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	fc.client.ConnectWithRetry()

	// Ждём подключения с таймаутом
	timer := time.NewTimer(100 * time.Millisecond)
	defer timer.Stop()

	for !fc.client.IsConnected() {
		timer.Reset(100 * time.Millisecond)
		select {
		case <-ctx.Done():
			return fmt.Errorf("esl connect timeout")
		case <-timer.C:
			return nil
		}
	}
	return nil
}

func (fc *FSCollector) Collect() (*PBXResult, error) {
	if !fc.client.IsConnected() {
		return nil, fmt.Errorf("esl не подключён")
	}

	result := &PBXResult{Status: "ok"}

	// active_calls: api show channels count
	if active, err := fc.apiInt("show channels count"); err == nil {
		result.ActiveCalls = active
	}

	// max_calls: api json status → max_sessions
	if max, err := fc.apiJSONInt("json", "status", "max_sessions"); err == nil {
		result.MaxCalls = max
	}

	// idle_cpu: eval $${idle_cpu} — FS-специфичный
	if idle, err := fc.apiFloat("eval $${idle_cpu}"); err == nil && idle > 0 {
		result.IdleCPU = idle
	}

	return result, nil
}

func (fc *FSCollector) ListenHangups(onHangup func(success bool)) {
	// Подписываемся на CHANNEL_HANGUP через ESL
	fc.client.Subscribe("CHANNEL_HANGUP")

	go func() {
		// Для FS используем обработку событий через eventsCh
		// Упрощённо: не реализуем полноценную подписку на первом этапе
		// failure rate остаётся на нуле
		time.Sleep(1 * time.Second)
	}()
}

func (fc *FSCollector) Close() {
	fc.client.Disconnect()
}

// --- helpers ---

func (fc *FSCollector) apiInt(cmd string) (int, error) {
	headers, body, err := fc.client.Send("api " + cmd)
	if err != nil {
		return 0, err
	}
	_ = headers
	val := strings.TrimSpace(body)
	n, err := strconv.Atoi(val)
	if err != nil {
		// иногда ответ содержит текст помимо числа
		n, _ = strconv.Atoi(strings.Fields(val)[0])
	}
	return n, nil
}

func (fc *FSCollector) apiFloat(cmd string) (float64, error) {
	_, body, err := fc.client.Send("api " + cmd)
	if err != nil {
		return 0, err
	}
	val := strings.TrimSpace(body)
	f, _ := strconv.ParseFloat(val, 64)
	return f, nil
}

// apiJSONInt парсит JSON-ответ от FS api: api json status → field1.field2
func (fc *FSCollector) apiJSONInt(cmd string, field string, subfield string) (int, error) {
	_, body, err := fc.client.Send("api " + cmd)
	if err != nil {
		return 0, err
	}
	// Быстрый парсинг: ищем "subfield": число
	search := fmt.Sprintf(`"%s":`, subfield)
	idx := strings.Index(body, search)
	if idx < 0 {
		return 0, fmt.Errorf("поле %s не найдено в ответе", subfield)
	}
	rest := body[idx+len(search):]
	rest = strings.TrimLeft(rest, " \t")
	end := strings.IndexAny(rest, ",\n\r}")
	if end > 0 {
		rest = rest[:end]
	}
	return strconv.Atoi(strings.TrimSpace(rest))
}
