package utils

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"sort"
	"strings"
	"time"

	"gorm.io/datatypes"
)

// Global HTTP Client for HA
var haHttpClient = &http.Client{
	Timeout: 10 * time.Second,
	Transport: &http.Transport{
		MaxIdleConns:        100,
		MaxIdleConnsPerHost: 10,
		IdleConnTimeout:     90 * time.Second,
	},
}

type HAConfig struct {
	URL   string `json:"url"`
	Token string `json:"token"`
}

type HAEntity struct {
	EntityID    string                 `json:"entity_id"`
	State       string                 `json:"state"`
	Attributes  map[string]interface{} `json:"attributes"`
	LastChanged time.Time              `json:"last_changed"`
	LastUpdated time.Time              `json:"last_updated"`
	DeviceID    string                 `json:"device_id"`
	DeviceName  string                 `json:"device_name"`
}

type HATemplateReq struct {
	Template string `json:"template"`
}

type HATemplateResult struct {
	ID    string `json:"id"`
	State string `json:"s"`
	Name  string `json:"n"`
	DID   string `json:"did"`
	DName string `json:"dn"`
}

// FetchHAEntitiesByDevice fetches entities for a specific device using HA Template API
func FetchHAEntitiesByDevice(config datatypes.JSON, deviceID string) ([]HAEntity, error) {
	haConfig, err := parseHAConfig(config)
	if err != nil {
		return nil, err
	}

	url := normalizeURL(haConfig.URL)

	rawTemplate := `
{% set ns = namespace(result=[]) %}
{% set device_entities = device_entities('__DEVICE_ID__') %}
{% for entity_id in device_entities %}
  {% set state = states[entity_id] %}
  {% if state %}
    {% set name = state.attributes.friendly_name %}
    {% if name is not defined or name is none %}
      {% set name = entity_id %}
    {% endif %}
    {% set entry = {
      "id": entity_id,
      "s": state.state,
      "n": name,
      "did": '__DEVICE_ID__',
      "dn": ''
    } %}
    {% set ns.result = ns.result + [entry] %}
  {% endif %}
{% endfor %}
{{ ns.result | to_json }}
`
	template := strings.ReplaceAll(rawTemplate, "__DEVICE_ID__", deviceID)

	return executeTemplateQuery(haHttpClient, url, haConfig.Token, template)
}

// FetchEntityState fetches a single entity state
func FetchEntityState(config datatypes.JSON, entityID string) (*HAEntity, error) {
	haConfig, err := parseHAConfig(config)
	if err != nil {
		return nil, err
	}

	url := normalizeURL(haConfig.URL)
	
	req, err := http.NewRequest("GET", fmt.Sprintf("%s/api/states/%s", url, entityID), nil)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Authorization", "Bearer "+haConfig.Token)
	req.Header.Set("Content-Type", "application/json")

	resp, err := haHttpClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		return nil, fmt.Errorf("HA returned status: %s", resp.Status)
	}

	var entity HAEntity
	if err := json.NewDecoder(resp.Body).Decode(&entity); err != nil {
		return nil, err
	}
	return &entity, nil
}

// BatchFetchStates fetches multiple entities in one go using states API
func BatchFetchStates(config datatypes.JSON, entityIDs []string) (map[string]string, error) {
	if len(entityIDs) == 0 {
		return map[string]string{}, nil
	}
	
	haConfig, err := parseHAConfig(config)
	if err != nil {
		return nil, err
	}

	url := normalizeURL(haConfig.URL)

	// Direct State API fetch - more reliable than Template for large lists or weird IDs
	req, err := http.NewRequest("GET", url+"/api/states", nil)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Authorization", "Bearer "+haConfig.Token)
	req.Header.Set("Content-Type", "application/json")

	fmt.Printf("DEBUG: Fetching all states from %s\n", url+"/api/states")

	resp, err := haHttpClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		return nil, fmt.Errorf("HA error: %s", resp.Status)
	}

	// Helper struct for decoding the list response
	type StateItem struct {
		EntityID string `json:"entity_id"`
		State    string `json:"state"`
	}

	var allStates []StateItem
	if err := json.NewDecoder(resp.Body).Decode(&allStates); err != nil {
		return nil, fmt.Errorf("decode error: %v", err)
	}

	fmt.Printf("DEBUG: Got %d total states from HA\n", len(allStates))

	// Convert list to map for filtering
	resultMap := make(map[string]string)
	
	// Create a lookup set for requested IDs
	targetIDs := make(map[string]bool)
	for _, id := range entityIDs {
		targetIDs[id] = true
	}

	// Filter
	foundCount := 0
	for _, item := range allStates {
		if targetIDs[item.EntityID] {
			// Basic filtering for invalid states
			if item.State != "unknown" && item.State != "unavailable" && item.State != "" {
				resultMap[item.EntityID] = item.State
				foundCount++
			} else {
				fmt.Printf("DEBUG: Entity %s found but state is '%s'\n", item.EntityID, item.State)
			}
		}
	}
	
	if foundCount == 0 {
		fmt.Printf("DEBUG: Warning - No matching entities found in HA response out of %d requested.\n", len(entityIDs))
		// Optional: Print a few available IDs to check for mismatch
		if len(allStates) > 0 {
			fmt.Printf("DEBUG: Sample available ID: %s\n", allStates[0].EntityID)
		}
	}

	return resultMap, nil
}

// Helpers

func parseHAConfig(config datatypes.JSON) (HAConfig, error) {
	var haConfig HAConfig
	b, err := config.MarshalJSON()
	if err != nil {
		return haConfig, fmt.Errorf("config error: %v", err)
	}
	if err := json.Unmarshal(b, &haConfig); err != nil {
		return haConfig, fmt.Errorf("invalid configuration format: %v", err)
	}
	if haConfig.URL == "" || haConfig.Token == "" {
		return haConfig, fmt.Errorf("URL and Token are required")
	}
	return haConfig, nil
}

func normalizeURL(url string) string {
	url = strings.TrimSuffix(url, "/")
	url = strings.TrimSuffix(url, "/api")
	return url
}

func executeTemplateQuery(client *http.Client, url, token, template string) ([]HAEntity, error) {
	reqBody, _ := json.Marshal(HATemplateReq{Template: template})
	req, err := http.NewRequest("POST", url+"/api/template", bytes.NewBuffer(reqBody))
	if err != nil {
		return nil, fmt.Errorf("failed to create request: %v", err)
	}
	req.Header.Set("Authorization", "Bearer "+token)
	req.Header.Set("Content-Type", "application/json")

	resp, err := client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("connection failed: %v", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		return nil, fmt.Errorf("Home Assistant returned status: %s", resp.Status)
	}

	var tmplResults []HATemplateResult
	if err := json.NewDecoder(resp.Body).Decode(&tmplResults); err != nil {
		return nil, fmt.Errorf("failed to decode response: %v", err)
	}

	entities := make([]HAEntity, len(tmplResults))
	for i, r := range tmplResults {
		entities[i] = HAEntity{
			EntityID:   r.ID,
			State:      r.State,
			Attributes: map[string]interface{}{"friendly_name": r.Name},
			DeviceID:   r.DID,
			DeviceName: r.DName,
		}
	}

	sort.Slice(entities, func(i, j int) bool {
		nameI, _ := entities[i].Attributes["friendly_name"].(string)
		nameJ, _ := entities[j].Attributes["friendly_name"].(string)
		if nameI == "" { nameI = entities[i].EntityID }
		if nameJ == "" { nameJ = entities[j].EntityID }
		return nameI < nameJ
	})

	return entities, nil
}
