package controllers

import (
	"fmt"
	"ems-backend/db"
	"ems-backend/models"
	"encoding/json"
	"net/http"
	"sort"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"gorm.io/datatypes"
	"bytes"
	"io"
	"strings"

	"github.com/xuri/excelize/v2"
)

// --- Integration Source Controllers ---

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"`   // Augmented field
	DeviceName  string                 `json:"device_name"` // Augmented field
}


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

// Struct for template result parsing
type HATemplateResult struct {
	ID    string `json:"id"`
	State string `json:"s"`
	Name  string `json:"n"`
	DID   string `json:"did"`
	DName string `json:"dn"`
}

// HADevice represents a Home Assistant Device
type HADevice struct {
	ID           string `json:"id"`
	Name         string `json:"name"`
	Model        string `json:"model"`
	Manufacturer string `json:"manufacturer"`
}

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

	client := &http.Client{Timeout: 10 * time.Second}
	url := haConfig.URL
	// Robust URL handling: remove trailing slash and /api suffix
	url = strings.TrimSuffix(url, "/")
	url = strings.TrimSuffix(url, "/api")
	
	// Use Template API to get devices efficiently
	// Simplified template avoiding list.append due to sandbox restrictions
	template := `
{% set ns = namespace(result=[], devs=[]) %}
{% for state in states %}
  {% set d = device_id(state.entity_id) %}
  {% if d and d not in ns.devs %}
    {% set ns.devs = ns.devs + [d] %}
    {% set name = device_attr(d, 'name_by_user') %}
    {% if not name %}
      {% set name = device_attr(d, 'name') %}
    {% endif %}
    {% if not name %}
      {% set name = 'Unknown' %}
    {% endif %}
    {% set entry = {
      "id": d,
      "name": name,
      "model": device_attr(d, 'model') or "",
      "manufacturer": device_attr(d, 'manufacturer') or ""
    } %}
    {% set ns.result = ns.result + [entry] %}
  {% endif %}
{% endfor %}
{{ ns.result | to_json }}
`
	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 "+haConfig.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()

	// Read body for better error reporting
	bodyBytes, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("failed to read response body: %v", err)
	}

	if resp.StatusCode != 200 {
		fmt.Printf("DEBUG: HA Status Error: %s, Body: %s\n", resp.Status, string(bodyBytes))
		return nil, fmt.Errorf("Home Assistant returned status: %s. Body: %s", resp.Status, string(bodyBytes))
	}

	var devices []HADevice
	if err := json.Unmarshal(bodyBytes, &devices); err != nil {
		// Try to see if it's because empty result or format
		fmt.Printf("DEBUG: Failed to decode HA response: %s\nError: %v\n", string(bodyBytes), err)
		return nil, fmt.Errorf("failed to decode response: %v. Body: %s", err, string(bodyBytes))
	}
	
	fmt.Printf("DEBUG: Successfully fetched %d devices\n", len(devices))
	return devices, nil
}

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

	client := &http.Client{Timeout: 10 * time.Second}
	url := haConfig.URL
	// Robust URL handling
	url = strings.TrimSuffix(url, "/")
	url = strings.TrimSuffix(url, "/api")

	// Template to fetch entities for a specific device
	// Using strings.Replace to avoid fmt.Sprintf interpreting Jinja2 tags {% as format specifiers
	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)

	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 "+haConfig.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()

	// Read body for better error reporting
	bodyBytes, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("failed to read response body: %v", err)
	}

	if resp.StatusCode != 200 {
		fmt.Printf("DEBUG: HA Status Error (Entities): %s, Body: %s\n", resp.Status, string(bodyBytes))
		return nil, fmt.Errorf("Home Assistant returned status: %s. Body: %s", resp.Status, string(bodyBytes))
	}

	var tmplResults []HATemplateResult
	if err := json.Unmarshal(bodyBytes, &tmplResults); err != nil {
		fmt.Printf("DEBUG: Failed to decode HA response (Entities): %s\nError: %v\n", string(bodyBytes), err)
		return nil, fmt.Errorf("failed to decode response: %v. Body: %s", err, string(bodyBytes))
	}
	
	fmt.Printf("DEBUG: Successfully fetched %d entities for device %s\n", len(tmplResults), deviceID)

	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 by friendly_name
	sort.Slice(entities, func(i, j int) bool {
		nameI, okI := entities[i].Attributes["friendly_name"].(string)
		nameJ, okJ := entities[j].Attributes["friendly_name"].(string)
		if !okI {
			nameI = entities[i].EntityID
		}
		if !okJ {
			nameJ = entities[j].EntityID
		}
		return nameI < nameJ
	})

	return entities, nil
}

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

	client := &http.Client{Timeout: 10 * time.Second}
	url := haConfig.URL
	// Robust URL handling
	url = strings.TrimSuffix(url, "/")
	url = strings.TrimSuffix(url, "/api")

	// Try Template API first to get device info
	// Using namespace to avoid list.append security restriction
	template := `
{% set ns = namespace(result=[]) %}
{% for state in states %}
  {% set name = state.attributes.friendly_name %}
  {% if name is not defined or name is none %}
    {% set name = state.entity_id %}
  {% endif %}
  {% set d = device_id(state.entity_id) %}
  {% if d %}
    {% set d_name = device_attr(d, 'name_by_user') or device_attr(d, 'name') or 'Unknown' %}
    {% set entry = {
      "id": state.entity_id,
      "s": state.state,
      "n": name,
      "did": d,
      "dn": d_name
    } %}
    {% set ns.result = ns.result + [entry] %}
  {% else %}
    {% set entry = {
      "id": state.entity_id,
      "s": state.state,
      "n": name,
      "did": "",
      "dn": ""
    } %}
    {% set ns.result = ns.result + [entry] %}
  {% endif %}
{% endfor %}
{{ ns.result | to_json }}
`
	// Clean up newlines/spaces for template req? Not strictly needed for JSON but good practice
	// Actually JSON marshalling handles it.
	
	reqBody, _ := json.Marshal(HATemplateReq{Template: template})
	req, err := http.NewRequest("POST", url+"/api/template", bytes.NewBuffer(reqBody))
	if err == nil {
		req.Header.Set("Authorization", "Bearer "+haConfig.Token)
		req.Header.Set("Content-Type", "application/json")
		
		resp, err := client.Do(req)
		if err == nil && resp.StatusCode == 200 {
			defer resp.Body.Close()
			// Parse template result
			// HA returns string body which IS the rendered template (JSON)
			// But careful: sometimes it's plain text.
			// "to_json" filter ensures it's JSON.
			
			var tmplResults []HATemplateResult
			if err := json.NewDecoder(resp.Body).Decode(&tmplResults); err == nil {
				// Convert to HAEntity
				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}, // Simplified attributes
						DeviceID:   r.DID,
						DeviceName: r.DName,
					}
				}
				
				// Sort by friendly_name
				sort.Slice(entities, func(i, j int) bool {
					nameI, okI := entities[i].Attributes["friendly_name"].(string)
					nameJ, okJ := entities[j].Attributes["friendly_name"].(string)
					if !okI {
						nameI = entities[i].EntityID
					}
					if !okJ {
						nameJ = entities[j].EntityID
					}
					return nameI < nameJ
				})

				return entities, nil
			}
			// If decode failed, fallthrough to legacy method
		}
	}

	// Fallback to /api/states
	req, err = http.NewRequest("GET", url+"/api/states", nil)
	if err != nil {
		return nil, fmt.Errorf("failed to create request: %v", err)
	}
	req.Header.Set("Authorization", "Bearer "+haConfig.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 entities []HAEntity
	if err := json.NewDecoder(resp.Body).Decode(&entities); err != nil {
		return nil, fmt.Errorf("failed to decode response: %v", err)
	}

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

	return entities, nil
}

func testHAConnection(config datatypes.JSON) (bool, string) {
	var haConfig HAConfig
	b, err := config.MarshalJSON()
	if err != nil {
		return false, "Config error"
	}
	if err := json.Unmarshal(b, &haConfig); err != nil {
		return false, "Invalid configuration format"
	}
	if haConfig.URL == "" || haConfig.Token == "" {
		return false, "URL and Token are required"
	}

	client := &http.Client{Timeout: 5 * time.Second}
	// Removing trailing slash if present to avoid double slash
	url := haConfig.URL
	url = strings.TrimSuffix(url, "/")
	url = strings.TrimSuffix(url, "/api")
	
	req, err := http.NewRequest("GET", url+"/api/", nil)
	if err != nil {
		return false, "Failed to create request: " + err.Error()
	}
	req.Header.Set("Authorization", "Bearer "+haConfig.Token)
	req.Header.Set("Content-Type", "application/json")

	resp, err := client.Do(req)
	if err != nil {
		return false, "Connection failed: " + err.Error()
	}
	defer resp.Body.Close()

	if resp.StatusCode == 200 {
		return true, "Success"
	}
	return false, "Home Assistant returned status: " + resp.Status
}

func GetSources(c *gin.Context) {
	var sources []models.IntegrationSource
	if err := models.DB.Find(&sources).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	
	// Populate DeviceCount
	for i := range sources {
		var count int64
		models.DB.Model(&models.Device{}).Where("source_id = ?", sources[i].ID).Count(&count)
		sources[i].DeviceCount = count
	}
	
	c.JSON(http.StatusOK, sources)
}

func CreateSource(c *gin.Context) {
	var source models.IntegrationSource
	if err := c.ShouldBindJSON(&source); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	// DEBUG LOG
	fmt.Printf("DEBUG: CreateSource received: %+v, Status: %s\n", source, source.Status)

	if err := models.DB.Create(&source).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusCreated, source)
}

func UpdateSource(c *gin.Context) {
	id := c.Param("id")
	
	// Check for associated devices
	var count int64
	if err := models.DB.Model(&models.Device{}).Where("source_id = ?", id).Count(&count).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to check device association"})
		return
	}
	if count > 0 {
		c.JSON(http.StatusForbidden, gin.H{"error": "Cannot edit source with associated devices"})
		return
	}

	var source models.IntegrationSource
	if err := models.DB.First(&source, "id = ?", id).Error; err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "Source not found"})
		return
	}
	if err := c.ShouldBindJSON(&source); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	// DEBUG LOG
	fmt.Printf("DEBUG: UpdateSource received: %+v, Status: %s\n", source, source.Status)

	models.DB.Save(&source)
	c.JSON(http.StatusOK, source)
}

func DeleteSource(c *gin.Context) {
	id := c.Param("id")

	// Check for associated devices
	var count int64
	if err := models.DB.Model(&models.Device{}).Where("source_id = ?", id).Count(&count).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to check device association"})
		return
	}
	if count > 0 {
		c.JSON(http.StatusForbidden, gin.H{"error": "Cannot delete source with associated devices"})
		return
	}

	if err := models.DB.Delete(&models.IntegrationSource{}, "id = ?", id).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"message": "Source deleted"})
}

// TestSourceConnection 测试连接
func TestSourceConnection(c *gin.Context) {
	var source models.IntegrationSource
	// 允许直接传参测试，或者传 ID 测试已有
	if err := c.ShouldBindJSON(&source); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	var success bool
	var msg string

	switch source.DriverType {
	case "HOME_ASSISTANT":
		success, msg = testHAConnection(source.Config)
	default:
		// Mock others for now
		success = true
		msg = "Connection simulated (driver not implemented)"
	}

	if success {
		// If the source has an ID, update its status in DB
		if source.ID != uuid.Nil {
			models.DB.Model(&models.IntegrationSource{}).Where("id = ?", source.ID).Update("status", "ONLINE")
		}
		c.JSON(http.StatusOK, gin.H{"success": true, "message": "Connection successful"})
	} else {
		if source.ID != uuid.Nil {
			models.DB.Model(&models.IntegrationSource{}).Where("id = ?", source.ID).Update("status", "OFFLINE")
		}
		c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": msg})
	}
}

// SyncSource 同步数据
func SyncSource(c *gin.Context) {
	id := c.Param("id")
	// TODO: 触发异步任务同步设备
	c.JSON(http.StatusOK, gin.H{"message": "Sync started for source " + id})
}

// GetSourceDevices 获取设备列表
func GetSourceDevices(c *gin.Context) {
	id := c.Param("id")
	var source models.IntegrationSource
	if err := models.DB.First(&source, "id = ?", id).Error; err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "Source not found"})
		return
	}

	if source.DriverType != "HOME_ASSISTANT" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Only Home Assistant sources are supported"})
		return
	}

	devices, err := fetchHADevices(source.Config)
	if err != nil {
		fmt.Printf("DEBUG: GetSourceDevices error: %v\n", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, devices)
}

// GetSourceDeviceEntities 获取指定设备的实体列表
func GetSourceDeviceEntities(c *gin.Context) {
	id := c.Param("id")
	deviceID := c.Param("deviceId")
	
	var source models.IntegrationSource
	if err := models.DB.First(&source, "id = ?", id).Error; err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "Source not found"})
		return
	}

	if source.DriverType != "HOME_ASSISTANT" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Only Home Assistant sources are supported"})
		return
	}

	entities, err := fetchHAEntitiesByDevice(source.Config, deviceID)
	if err != nil {
		fmt.Printf("DEBUG: GetSourceDeviceEntities error: %v\n", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, entities)
}

// GetSourceCandidates 获取数据源候选设备列表 (Deprecated or kept for backward compat)
func GetSourceCandidates(c *gin.Context) {
	id := c.Param("id")
	var source models.IntegrationSource
	if err := models.DB.First(&source, "id = ?", id).Error; err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "Source not found"})
		return
	}

	if source.DriverType != "HOME_ASSISTANT" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Only Home Assistant sources are supported for candidate fetching currently"})
		return
	}

	entities, err := fetchHAEntities(source.Config)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, entities)
}

// CallSourceService 调用数据源服务 (Home Assistant Call Service)
type ServiceCallReq struct {
	Domain      string                 `json:"domain"`
	Service     string                 `json:"service"`
	ServiceData map[string]interface{} `json:"service_data"`
}

func CallSourceService(c *gin.Context) {
	id := c.Param("id")
	var req ServiceCallReq
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	var source models.IntegrationSource
	if err := models.DB.First(&source, "id = ?", id).Error; err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "Source not found"})
		return
	}

	if source.DriverType != "HOME_ASSISTANT" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Only Home Assistant sources are supported"})
		return
	}

	// Call HA API
	var haConfig HAConfig
	b, _ := source.Config.MarshalJSON()
	json.Unmarshal(b, &haConfig) // Error ignored as DB data should be valid

	if haConfig.URL == "" || haConfig.Token == "" {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid source configuration"})
		return
	}

	client := &http.Client{Timeout: 10 * time.Second}
	url := haConfig.URL
	url = strings.TrimSuffix(url, "/")
	url = strings.TrimSuffix(url, "/api")

	// Target URL: /api/services/<domain>/<service>
	targetURL := fmt.Sprintf("%s/api/services/%s/%s", url, req.Domain, req.Service)
	
	reqBody, _ := json.Marshal(req.ServiceData)
	httpReq, err := http.NewRequest("POST", targetURL, bytes.NewBuffer(reqBody))
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create request: " + err.Error()})
		return
	}
	httpReq.Header.Set("Authorization", "Bearer "+haConfig.Token)
	httpReq.Header.Set("Content-Type", "application/json")

	resp, err := client.Do(httpReq)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Connection failed: " + err.Error()})
		return
	}
	defer resp.Body.Close()

	bodyBytes, _ := io.ReadAll(resp.Body)
	if resp.StatusCode != 200 && resp.StatusCode != 201 {
		c.JSON(http.StatusBadGateway, gin.H{"error": "HA Error", "details": string(bodyBytes)})
		return
	}

	// HA usually returns a list of state changes
	var result interface{}
	json.Unmarshal(bodyBytes, &result)
	
	c.JSON(http.StatusOK, result)
}

// SourceImportConfig for import mapping
type SourceImportConfig struct {
	DriverType string `json:"driver_type"`
	Mapping    struct {
		Name   string            `json:"name"`
		Config map[string]string `json:"config"`
	} `json:"mapping"`
}

type ImportError struct {
	Row   int    `json:"row"`
	Name  string `json:"name"`
	Error string `json:"error"`
}

type ImportResult struct {
	Total   int           `json:"total"`
	Success int           `json:"success"`
	Failed  int           `json:"failed"`
	Errors  []ImportError `json:"errors"`
}

// PreviewSourceImport handles Excel preview
func PreviewSourceImport(c *gin.Context) {
	file, err := c.FormFile("file")
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "No file uploaded"})
		return
	}

	f, err := file.Open()
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to open file"})
		return
	}
	defer f.Close()

	excel, err := excelize.OpenReader(f)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to parse Excel: " + err.Error()})
		return
	}
	defer excel.Close()

	// Get Sheet1
	sheets := excel.GetSheetList()
	if len(sheets) == 0 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Empty Excel file"})
		return
	}
	sheet := sheets[0]

	rows, err := excel.GetRows(sheet)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to read rows"})
		return
	}

	if len(rows) < 1 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "No data in Excel"})
		return
	}

	// Return first 10 rows raw data
	limit := 10
	if len(rows) < limit {
		limit = len(rows)
	}
	
	rawRows := rows[:limit]

	c.JSON(http.StatusOK, gin.H{
		"rows": rawRows,
	})
}

// ImportSources executes the import
func ImportSources(c *gin.Context) {
	file, err := c.FormFile("file")
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "No file uploaded"})
		return
	}
	configStr := c.PostForm("config")
	headerRowStr := c.PostForm("header_row")
	
	var config SourceImportConfig
	if err := json.Unmarshal([]byte(configStr), &config); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid mapping config"})
		return
	}
	
	headerRow := 1
	if headerRowStr != "" {
		fmt.Sscanf(headerRowStr, "%d", &headerRow)
	}
	if headerRow < 1 {
		headerRow = 1
	}

	f, err := file.Open()
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to open file"})
		return
	}
	defer f.Close()

	excel, err := excelize.OpenReader(f)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to parse Excel: " + err.Error()})
		return
	}
	defer excel.Close()

	sheets := excel.GetSheetList()
	if len(sheets) == 0 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Empty Excel file"})
		return
	}
	rows, err := excel.GetRows(sheets[0])
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to read rows"})
		return
	}

	if len(rows) < headerRow {
		c.JSON(http.StatusOK, gin.H{"message": "No data to import"})
		return
	}

	startRowIndex := headerRow - 1
	// headerMap logic removed as we use direct column mapping (A, B, C...)

	result := ImportResult{
		Total:   len(rows) - startRowIndex,
		Errors:  make([]ImportError, 0),
	}

	for i := startRowIndex; i < len(rows); i++ {
		row := rows[i]

		// Helper to get value by Excel Column Name (A, B, C...)
		getValueByCol := func(colName string) string {
			if colName == "" {
				return ""
			}
			colNum, err := excelize.ColumnNameToNumber(colName)
			if err != nil {
				return ""
			}
			idx := colNum - 1 // 1-based to 0-based
			if idx >= 0 && idx < len(row) {
				return row[idx]
			}
			return ""
		}

		// Get Name
		name := getValueByCol(config.Mapping.Name)
		if name == "" {
			result.Failed++
			result.Errors = append(result.Errors, ImportError{
				Row:   i + 1,
				Name:  "",
				Error: "Name is empty (Column " + config.Mapping.Name + ")",
			})
			continue
		}

		// Build Config
		conf := make(map[string]interface{})
		for confKey, colName := range config.Mapping.Config {
			val := getValueByCol(colName)
			if val != "" {
				conf[confKey] = val
			}
		}
		
		// If specific driver defaults are needed
		if config.DriverType == "MODBUS_TCP" {
			// Convert port to int if exists
			if p, ok := conf["port"]; ok {
				_ = p
			}
		}

		b, _ := json.Marshal(conf)
		
		source := models.IntegrationSource{
			ID:         uuid.New(), // Explicitly generate ID
			Name:       name,
			DriverType: config.DriverType,
			Status:     "OFFLINE",
			Config:     datatypes.JSON(b),
		}
		
		if err := models.DB.Create(&source).Error; err != nil {
			fmt.Printf("[IMPORT] Failed to import row %d: %v\n", i+1, err)
			result.Failed++
			result.Errors = append(result.Errors, ImportError{
				Row:   i + 1,
				Name:  name,
				Error: err.Error(),
			})
			continue
		}
		fmt.Printf("[IMPORT] Successfully imported source: %s (ID: %s)\n", source.Name, source.ID)
		result.Success++
	}

	// Structured Log
	fmt.Printf("[IMPORT] Summary: Total=%d, Success=%d, Failed=%d\n", result.Total, result.Success, result.Failed)
	if len(result.Errors) > 0 {
		fmt.Printf("[IMPORT] Errors: %+v\n", result.Errors)
	}

	c.JSON(http.StatusOK, result)
}

// --- Device Controllers ---

func GetDevices(c *gin.Context) {
	var devices []models.Device
	// Support filtering by location_id or source_id if provided
	locationID := c.Query("location_id")
	sourceID := c.Query("source_id")
	name := c.Query("name")
	deviceType := c.Query("device_type")
	status := c.Query("status")
	
	query := models.DB
	if locationID != "" {
		query = query.Where("location_id = ?", locationID)
	}
	if sourceID != "" {
		query = query.Where("source_id = ?", sourceID)
	}
	if name != "" {
		query = query.Where("name ILIKE ?", "%"+name+"%")
	}
	if deviceType != "" {
		query = query.Where("device_type = ?", deviceType)
	}
	if status != "" {
		query = query.Where("status = ?", status)
	}
	
	if err := query.Order("name ASC").Find(&devices).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, devices)
}

func GetDevice(c *gin.Context) {
	id := c.Param("id")
	var device models.Device
	if err := models.DB.First(&device, "id = ?", id).Error; err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "Device not found"})
		return
	}
	c.JSON(http.StatusOK, device)
}

func GetDeviceRealtime(c *gin.Context) {
	id := c.Param("id")
	
	// Fetch latest data from TDengine (Cleaned Data)
	data, err := db.GetLatestDeviceData(id)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	
	c.JSON(http.StatusOK, data)
}

func CreateDevice(c *gin.Context) {
	var device models.Device
	if err := c.ShouldBindJSON(&device); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	// Check for duplicate ExternalID in the same source
	var count int64
	if err := models.DB.Model(&models.Device{}).Where("source_id = ? AND external_id = ?", device.SourceID, device.ExternalID).Count(&count).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to check for duplicates"})
		return
	}
	if count > 0 {
		c.JSON(http.StatusConflict, gin.H{"error": "Device with this ExternalID already exists in the source"})
		return
	}

	if err := models.DB.Create(&device).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusCreated, device)
}

func UpdateDevice(c *gin.Context) {
	id := c.Param("id")
	var device models.Device
	if err := models.DB.First(&device, "id = ?", id).Error; err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "Device not found"})
		return
	}
	if err := c.ShouldBindJSON(&device); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	models.DB.Save(&device)
	c.JSON(http.StatusOK, device)
}

func DeleteDevice(c *gin.Context) {
	id := c.Param("id")
	if err := models.DB.Delete(&models.Device{}, "id = ?", id).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"message": "Device deleted"})
}

// --- Location Controllers ---

func GetLocations(c *gin.Context) {
	var locations []models.SysLocation
	if err := models.DB.Find(&locations).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, locations)
}

func CreateLocation(c *gin.Context) {
	var location models.SysLocation
	if err := c.ShouldBindJSON(&location); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	if err := models.DB.Create(&location).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusCreated, location)
}

func UpdateLocation(c *gin.Context) {
	id := c.Param("id")
	var location models.SysLocation
	if err := models.DB.First(&location, "id = ?", id).Error; err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "Location not found"})
		return
	}
	if err := c.ShouldBindJSON(&location); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	models.DB.Save(&location)
	c.JSON(http.StatusOK, location)
}

func DeleteLocation(c *gin.Context) {
	id := c.Param("id")

	// 1. Check for child locations
	var childCount int64
	if err := models.DB.Model(&models.SysLocation{}).Where("parent_id = ?", id).Count(&childCount).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to check child locations"})
		return
	}
	if childCount > 0 {
		c.JSON(http.StatusForbidden, gin.H{"error": "Cannot delete location with child locations. Please delete children first."})
		return
	}

	// 2. Check for associated devices
	var deviceCount int64
	if err := models.DB.Model(&models.Device{}).Where("location_id = ?", id).Count(&deviceCount).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to check associated devices"})
		return
	}
	if deviceCount > 0 {
		c.JSON(http.StatusForbidden, gin.H{"error": "Cannot delete location with associated devices. Please remove devices first."})
		return
	}

	if err := models.DB.Delete(&models.SysLocation{}, "id = ?", id).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"message": "Location deleted"})
}

// GetDeviceHistory fetches historical data for devices
func GetDeviceHistory(c *gin.Context) {
	// Parse Query Params
	// device_ids (comma separated)
	// metric (default power)
	// start, end (timestamps or ISO strings)
	// interval (e.g. 1m, 1h)

	deviceIDsStr := c.Query("device_ids")
	if deviceIDsStr == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "device_ids is required"})
		return
	}
	deviceIDs := strings.Split(deviceIDsStr, ",")

	metric := c.Query("metric")
	// if metric == "" {
	// 	metric = "power"
	// }

	startStr := c.Query("start")
	endStr := c.Query("end")
	interval := c.Query("interval")
	if interval == "" {
		interval = "raw"
	}

	// Default time range: last 24h
	end := time.Now()
	start := end.Add(-24 * time.Hour)

	if startStr != "" {
		if t, err := time.Parse(time.RFC3339, startStr); err == nil {
			start = t
		} else if t, err := time.ParseInLocation("2006-01-02 15:04:05", startStr, time.Local); err == nil {
			start = t
		}
	}
	if endStr != "" {
		if t, err := time.Parse(time.RFC3339, endStr); err == nil {
			end = t
		} else if t, err := time.ParseInLocation("2006-01-02 15:04:05", endStr, time.Local); err == nil {
			end = t
		}
	}

	data, err := db.GetReadings(deviceIDs, metric, start, end, interval)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, data)
}

// DeleteDeviceHistory deletes history records in a range
func DeleteDeviceHistory(c *gin.Context) {
	// Query params: device_id, metric, start, end
	deviceID := c.Query("device_id")
	metric := c.Query("metric")
	startStr := c.Query("start")
	endStr := c.Query("end")

	if deviceID == "" || metric == "" || startStr == "" || endStr == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "device_id, metric, start, and end are required"})
		return
	}

	var start, end time.Time
	var err error

	// Parse Start
	if start, err = time.ParseInLocation("2006-01-02 15:04:05", startStr, time.Local); err != nil {
		if start, err = time.Parse(time.RFC3339, startStr); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid start format"})
			return
		}
	}

	// Parse End
	if end, err = time.ParseInLocation("2006-01-02 15:04:05", endStr, time.Local); err != nil {
		if end, err = time.Parse(time.RFC3339, endStr); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid end format"})
			return
		}
	}

	if err := db.DeleteReadings(deviceID, metric, start, end); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"message": "Readings deleted"})
}
