package api

import (
	"fmt"
	"log"
	"net/http"
	"sort"
	"strings"
	"time"

	"github.com/pulse-lets-go/internal/config"
	"github.com/pulse-lets-go/internal/models"
)

// handleGetTrunks — GET /api/trunks — список всех транков с опциональным фильтром.
func (a *API) handleGetTrunks(w http.ResponseWriter, r *http.Request) {
	trunks, err := a.configManager.ReadTrunks()
	if err != nil {
		writeError(w, http.StatusInternalServerError, "ошибка чтения транков")
		return
	}

	// Фильтр по типу
	typeFilter := r.URL.Query().Get("type")
	if typeFilter != "" {
		filtered := make([]models.Trunk, 0)
		for _, t := range trunks {
			if string(t.Type) == typeFilter {
				filtered = append(filtered, t)
			}
		}
		trunks = filtered
	}

	// Сортировка по created_at
	sort.Slice(trunks, func(i, j int) bool {
		return trunks[i].CreatedAt.Before(trunks[j].CreatedAt)
	})

	writeJSON(w, http.StatusOK, trunks)
}

// handleCreateTrunk — POST /api/trunks — создание нового транка.
func (a *API) handleCreateTrunk(w http.ResponseWriter, r *http.Request) {
	var req models.CreateTrunkRequest
	if err := decodeJSON(r, &req); err != nil {
		writeError(w, http.StatusBadRequest, "некорректный JSON")
		return
	}

	if err := validateTrunk(req.Name, req.Type, req.Gateway); err != nil {
		writeError(w, http.StatusBadRequest, err.Error())
		return
	}

	// Проверяем: fallback может быть только один
	trunks, err := a.configManager.ReadTrunks()
	if err != nil {
		writeError(w, http.StatusInternalServerError, "ошибка чтения транков")
		return
	}
	if req.Type == models.TrunkFallback {
		for _, t := range trunks {
			if t.Type == models.TrunkFallback {
				writeError(w, http.StatusConflict, "fallback-транк уже существует (должен быть ровно 1)")
				return
			}
		}
	}

	now := time.Now().UTC()
	trunk := models.Trunk{
		ID:          generateID("trk"),
		Name:        req.Name,
		Type:        req.Type,
		NodeID:      req.NodeID,
		Gateway:     req.Gateway,
		Codecs:      req.Codecs,
		Context:     req.Context,
		Enabled:     req.Enabled,
		Description: req.Description,
		CreatedAt:   now,
		UpdatedAt:   now,
	}

	trunks = append(trunks, trunk)
	if err := a.configManager.SaveTrunks(trunks); err != nil {
		writeError(w, http.StatusInternalServerError, "ошибка сохранения транка")
		return
	}

	// ESL push: создаём gateway на FS для ingress-транка
	a.eslPushGatewayAdd(trunk)

	a.RebuildTrunkCache()

	writeJSON(w, http.StatusCreated, trunk)
}

// handleUpdateTrunk — PUT /api/trunks/{id} — обновление транка.
func (a *API) handleUpdateTrunk(w http.ResponseWriter, r *http.Request) {
	trunkID := r.PathValue("id")
	if trunkID == "" {
		writeError(w, http.StatusBadRequest, "не указан id транка")
		return
	}

	var req models.UpdateTrunkRequest
	if err := decodeJSON(r, &req); err != nil {
		writeError(w, http.StatusBadRequest, "некорректный JSON")
		return
	}

	trunks, err := a.configManager.ReadTrunks()
	if err != nil {
		writeError(w, http.StatusInternalServerError, "ошибка чтения транков")
		return
	}

	idx := findTrunkIndex(trunks, trunkID)
	if idx < 0 {
		writeError(w, http.StatusNotFound, "транк не найден")
		return
	}

	// Применяем частичные обновления
	t := &trunks[idx]
	if req.Name != nil {
		t.Name = *req.Name
	}
	if req.Type != nil {
		// Проверка fallback-уникальности
		if *req.Type == models.TrunkFallback && t.Type != models.TrunkFallback {
			for _, other := range trunks {
				if other.Type == models.TrunkFallback && other.ID != trunkID {
					writeError(w, http.StatusConflict, "fallback-транк уже существует")
					return
				}
			}
		}
		t.Type = *req.Type
	}
	if req.NodeID != nil {
		t.NodeID = *req.NodeID
	}
	if req.Gateway != nil {
		t.Gateway = *req.Gateway
	}
	if req.Codecs != nil {
		t.Codecs = req.Codecs
	}
	if req.Context != nil {
		t.Context = *req.Context
	}
	if req.Enabled != nil {
		t.Enabled = *req.Enabled
	}
	if req.Description != nil {
		t.Description = *req.Description
	}
	t.UpdatedAt = time.Now().UTC()

	if err := a.configManager.SaveTrunks(trunks); err != nil {
		writeError(w, http.StatusInternalServerError, "ошибка сохранения транка")
		return
	}

	// ESL push: обновляем gateway на FS
	a.eslPushGatewayUpdate(*t)

	a.RebuildTrunkCache()

	writeJSON(w, http.StatusOK, t)
}

// handleDeleteTrunk — DELETE /api/trunks/{id} — удаление транка.
func (a *API) handleDeleteTrunk(w http.ResponseWriter, r *http.Request) {
	trunkID := r.PathValue("id")
	if trunkID == "" {
		writeError(w, http.StatusBadRequest, "не указан id транка")
		return
	}

	trunks, err := a.configManager.ReadTrunks()
	if err != nil {
		writeError(w, http.StatusInternalServerError, "ошибка чтения транков")
		return
	}

	idx := findTrunkIndex(trunks, trunkID)
	if idx < 0 {
		writeError(w, http.StatusNotFound, "транк не найден")
		return
	}

	deletedTrunk := trunks[idx]
	trunks = append(trunks[:idx], trunks[idx+1:]...)

	// ESL push: удаляем gateway на FS
	a.eslPushGatewayDelete(deletedTrunk)

	if err := a.configManager.SaveTrunks(trunks); err != nil {
		writeError(w, http.StatusInternalServerError, "ошибка сохранения транков")
		return
	}

	a.RebuildTrunkCache()

	writeJSON(w, http.StatusNoContent, nil)
}

// --- helpers ---

func validateTrunk(name string, trunkType models.TrunkType, gateway string) error {
	if name == "" {
		return fmt.Errorf("имя транка обязательно")
	}
	if gateway == "" {
		return fmt.Errorf("gateway обязателен")
	}
	switch trunkType {
	case models.TrunkIngress, models.TrunkBalance, models.TrunkFallback:
		break
	default:
		return fmt.Errorf("некорректный тип транка: %s", trunkType)
	}
	return nil
}

func findTrunkIndex(trunks []models.Trunk, id string) int {
	for i := range trunks {
		if trunks[i].ID == id {
			return i
		}
	}
	return -1
}

// --- ESL push helpers ---

// eslPushGatewayAdd создаёт gateway на FS для ingress-транка.
func (a *API) eslPushGatewayAdd(trunk models.Trunk) {
	if a.eslClient == nil || !a.eslClient.IsConnected() || trunk.Type != models.TrunkIngress {
		return
	}
	profile, name, proxy := a.gatewayParams(trunk)
	if err := a.eslClient.GatewayAdd(profile, name, proxy); err != nil {
		log.Printf("[esl] GatewayAdd %s: %v", name, err)
	}
}

// eslPushGatewayUpdate обновляет gateway на FS (удаляет старый + создаёт новый).
func (a *API) eslPushGatewayUpdate(trunk models.Trunk) {
	if a.eslClient == nil || !a.eslClient.IsConnected() || trunk.Type != models.TrunkIngress {
		return
	}
	profile, name, proxy := a.gatewayParams(trunk)
	_ = a.eslClient.GatewayDelete(profile, name) // игнорируем ошибку — может не существовать
	if err := a.eslClient.GatewayAdd(profile, name, proxy); err != nil {
		log.Printf("[esl] GatewayUpdate %s: %v", name, err)
	}
}

// eslPushGatewayDelete удаляет gateway на FS для ingress-транка.
func (a *API) eslPushGatewayDelete(trunk models.Trunk) {
	if a.eslClient == nil || !a.eslClient.IsConnected() || trunk.Type != models.TrunkIngress {
		return
	}
	profile, name, _ := a.gatewayParams(trunk)
	if err := a.eslClient.GatewayDelete(profile, name); err != nil {
		log.Printf("[esl] GatewayDelete %s: %v", name, err)
	}
}

// gatewayParams возвращает параметры для создания FS gateway на основе транка.
func (a *API) gatewayParams(trunk models.Trunk) (profile, name, proxy string) {
	cfg := a.readConfig()
	profile = cfg.ESL.SofiaProfile
	name = fmt.Sprintf("%s-ingress-%s", cfg.ESL.GatewayPrefix, trunk.ID)
	proxy = extractHost(trunk.Gateway)
	return
}

// extractHost извлекает хост из SIP URI.
// "sip:mts-gw.lan:5060" → "mts-gw.lan"
func extractHost(gateway string) string {
	s := strings.TrimPrefix(gateway, "sip:")
	s = strings.TrimPrefix(s, "sips:")
	if idx := strings.LastIndex(s, ":"); idx > 0 {
		// Проверяем, порт ли это (все цифры после двоеточия)
		port := s[idx+1:]
		isNumeric := true
		for _, c := range port {
			if c < '0' || c > '9' {
				isNumeric = false
				break
			}
		}
		if isNumeric {
			s = s[:idx]
		}
	}
	return s
}

// readConfig возвращает текущий конфиг (для получения esl.sofia_profile).
func (a *API) readConfig() *config.Config {
	cfg, _ := a.configManager.ReadConfig()
	if cfg == nil {
		return &config.Config{ESL: config.ESLConfig{SofiaProfile: "external"}}
	}
	return cfg
}
