package server

import (
	"database/sql"
	"encoding/binary"
	"fmt"
	"math"
	"net/http"
	"time"
	"weatherstation/internal/config"
	"weatherstation/internal/database"

	"github.com/gin-gonic/gin"
)

type radarTileRecord struct {
	DT     time.Time
	Z      int
	Y      int
	X      int
	Width  int
	Height int
	West   float64
	South  float64
	East   float64
	North  float64
	ResDeg float64
	Data   []byte
}

type radarTileResponse struct {
	DT     string       `json:"dt"`
	Z      int          `json:"z"`
	Y      int          `json:"y"`
	X      int          `json:"x"`
	Width  int          `json:"width"`
	Height int          `json:"height"`
	West   float64      `json:"west"`
	South  float64      `json:"south"`
	East   float64      `json:"east"`
	North  float64      `json:"north"`
	ResDeg float64      `json:"res_deg"`
	Values [][]*float64 `json:"values"` // null 表示无效值
}

func getLatestRadarTile(db *sql.DB, z, y, x int) (*radarTileRecord, error) {
	const q = `
        SELECT dt, z, y, x, width, height, west, south, east, north, res_deg, data
        FROM radar_tiles
        WHERE z=$1 AND y=$2 AND x=$3
        ORDER BY dt DESC
        LIMIT 1`
	var r radarTileRecord
	err := db.QueryRow(q, z, y, x).Scan(
		&r.DT, &r.Z, &r.Y, &r.X, &r.Width, &r.Height,
		&r.West, &r.South, &r.East, &r.North, &r.ResDeg, &r.Data,
	)
	if err != nil {
		return nil, err
	}
	return &r, nil
}

func getRadarTileAt(db *sql.DB, z, y, x int, dt time.Time) (*radarTileRecord, error) {
	const q = `
        SELECT dt, z, y, x, width, height, west, south, east, north, res_deg, data
        FROM radar_tiles
        WHERE z=$1 AND y=$2 AND x=$3 AND dt=$4
        LIMIT 1`
	var r radarTileRecord
	err := db.QueryRow(q, z, y, x, dt).Scan(
		&r.DT, &r.Z, &r.Y, &r.X, &r.Width, &r.Height,
		&r.West, &r.South, &r.East, &r.North, &r.ResDeg, &r.Data,
	)
	if err != nil {
		return nil, err
	}
	return &r, nil
}

// latestRadarTileHandler 返回指定 z/y/x 的最新瓦片，包含栅格 dBZ 值及元数据
func latestRadarTileHandler(c *gin.Context) {
	// 固定默认 7/40/102，可通过查询参数覆盖
	z := parseIntDefault(c.Query("z"), 7)
	y := parseIntDefault(c.Query("y"), 40)
	x := parseIntDefault(c.Query("x"), 102)

	rec, err := getLatestRadarTile(database.GetDB(), z, y, x)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "未找到雷达瓦片"})
		return
	}

	// 解码大端 int16 → dBZ (raw/10). >=32766 视为无效(null)
	w, h := rec.Width, rec.Height
	vals := make([][]*float64, h)
	// 每行 256 单元，每单元 2 字节
	if len(rec.Data) < w*h*2 {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "数据长度异常"})
		return
	}
	off := 0
	for row := 0; row < h; row++ {
		rowVals := make([]*float64, w)
		for col := 0; col < w; col++ {
			v := int16(binary.BigEndian.Uint16(rec.Data[off : off+2]))
			off += 2
			if v >= 32766 {
				rowVals[col] = nil
				continue
			}
			dbz := float64(v) / 10.0
			// 限幅到 [0,75]（大部分 CREF 标准范围），便于颜色映射
			if dbz < 0 {
				dbz = 0
			} else if dbz > 75 {
				dbz = 75
			}
			vv := dbz
			rowVals[col] = &vv
		}
		vals[row] = rowVals
	}

	loc, _ := time.LoadLocation("Asia/Shanghai")
	if loc == nil {
		loc = time.FixedZone("CST", 8*3600)
	}
	resp := radarTileResponse{
		DT:     rec.DT.In(loc).Format("2006-01-02 15:04:05"),
		Z:      rec.Z,
		Y:      rec.Y,
		X:      rec.X,
		Width:  rec.Width,
		Height: rec.Height,
		West:   rec.West,
		South:  rec.South,
		East:   rec.East,
		North:  rec.North,
		ResDeg: rec.ResDeg,
		Values: vals,
	}
	c.JSON(http.StatusOK, resp)
}

// radarTileAtHandler 返回指定 z/y/x 的指定时间瓦片
func radarTileAtHandler(c *gin.Context) {
	z := parseIntDefault(c.Query("z"), 7)
	y := parseIntDefault(c.Query("y"), 40)
	x := parseIntDefault(c.Query("x"), 102)
	dtStr := c.Query("dt")
	if dtStr == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "缺少 dt 参数（YYYY-MM-DD HH:MM:SS）"})
		return
	}
	loc, _ := time.LoadLocation("Asia/Shanghai")
	if loc == nil {
		loc = time.FixedZone("CST", 8*3600)
	}
	dt, err := time.ParseInLocation("2006-01-02 15:04:05", dtStr, loc)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "无效的 dt 格式"})
		return
	}
	rec, err := getRadarTileAt(database.GetDB(), z, y, x, dt)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "未找到指定时间瓦片"})
		return
	}
	w, h := rec.Width, rec.Height
	if len(rec.Data) < w*h*2 {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "数据长度异常"})
		return
	}
	vals := make([][]*float64, h)
	off := 0
	for row := 0; row < h; row++ {
		rowVals := make([]*float64, w)
		for col := 0; col < w; col++ {
			v := int16(binary.BigEndian.Uint16(rec.Data[off : off+2]))
			off += 2
			if v >= 32766 {
				rowVals[col] = nil
				continue
			}
			dbz := float64(v) / 10.0
			if dbz < 0 {
				dbz = 0
			} else if dbz > 75 {
				dbz = 75
			}
			vv := dbz
			rowVals[col] = &vv
		}
		vals[row] = rowVals
	}
	// 统一以 CST(+8) 输出
	resp := radarTileResponse{
		DT:     rec.DT.In(loc).Format("2006-01-02 15:04:05"),
		Z:      rec.Z,
		Y:      rec.Y,
		X:      rec.X,
		Width:  rec.Width,
		Height: rec.Height,
		West:   rec.West,
		South:  rec.South,
		East:   rec.East,
		North:  rec.North,
		ResDeg: rec.ResDeg,
		Values: vals,
	}
	c.JSON(http.StatusOK, resp)
}

// radarTileTimesHandler 返回指定 z/y/x 的可用时间列表（倒序）
func radarTileTimesHandler(c *gin.Context) {
	z := parseIntDefault(c.Query("z"), 7)
	y := parseIntDefault(c.Query("y"), 40)
	x := parseIntDefault(c.Query("x"), 102)
	fromStr := c.Query("from")
	toStr := c.Query("to")
	loc, _ := time.LoadLocation("Asia/Shanghai")
	if loc == nil {
		loc = time.FixedZone("CST", 8*3600)
	}
	var rows *sql.Rows
	var err error
	if fromStr != "" && toStr != "" {
		from, err1 := time.ParseInLocation("2006-01-02 15:04:05", fromStr, loc)
		to, err2 := time.ParseInLocation("2006-01-02 15:04:05", toStr, loc)
		if err1 != nil || err2 != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "无效的 from/to 时间格式"})
			return
		}
		const qRange = `
            SELECT dt FROM radar_tiles
            WHERE z=$1 AND y=$2 AND x=$3 AND dt BETWEEN $4 AND $5
            ORDER BY dt DESC`
		rows, err = database.GetDB().Query(qRange, z, y, x, from, to)
	} else {
		limit := parseIntDefault(c.Query("limit"), 48)
		const q = `
            SELECT dt FROM radar_tiles
            WHERE z=$1 AND y=$2 AND x=$3
            ORDER BY dt DESC
            LIMIT $4`
		rows, err = database.GetDB().Query(q, z, y, x, limit)
	}
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "查询时间列表失败"})
		return
	}
	defer rows.Close()
	var times []string
	for rows.Next() {
		var dt time.Time
		if err := rows.Scan(&dt); err != nil {
			continue
		}
		times = append(times, dt.In(loc).Format("2006-01-02 15:04:05"))
	}
	c.JSON(http.StatusOK, gin.H{"times": times})
}

// radarTilesAtHandler returns all radar tiles at the given dt for a specific z.
// It aggregates rows with the same z and dt but different y/x so the frontend can overlay them together.
// GET /api/radar/tiles_at?z=7&dt=YYYY-MM-DD HH:MM:SS
func radarTilesAtHandler(c *gin.Context) {
	z := parseIntDefault(c.Query("z"), 7)
	dtStr := c.Query("dt")
	if dtStr == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "缺少 dt 参数（YYYY-MM-DD HH:MM:SS）"})
		return
	}
	loc, _ := time.LoadLocation("Asia/Shanghai")
	if loc == nil {
		loc = time.FixedZone("CST", 8*3600)
	}
	dt, err := time.ParseInLocation("2006-01-02 15:04:05", dtStr, loc)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "无效的 dt 格式"})
		return
	}

	// Query all tiles at z + dt
	const q = `
        SELECT dt, z, y, x, width, height, west, south, east, north, res_deg, data
        FROM radar_tiles
        WHERE z=$1 AND dt=$2
        ORDER BY y, x`
	rows, qerr := database.GetDB().Query(q, z, dt)
	if qerr != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("数据库查询失败: %v", qerr)})
		return
	}
	defer rows.Close()

	var tiles []radarTileResponse
	for rows.Next() {
		var r radarTileRecord
		if err := rows.Scan(&r.DT, &r.Z, &r.Y, &r.X, &r.Width, &r.Height, &r.West, &r.South, &r.East, &r.North, &r.ResDeg, &r.Data); err != nil {
			continue
		}
		w, h := r.Width, r.Height
		if w <= 0 || h <= 0 || len(r.Data) < w*h*2 {
			continue
		}
		vals := make([][]*float64, h)
		off := 0
		for row := 0; row < h; row++ {
			rowVals := make([]*float64, w)
			for col := 0; col < w; col++ {
				v := int16(binary.BigEndian.Uint16(r.Data[off : off+2]))
				off += 2
				if v >= 32766 {
					rowVals[col] = nil
					continue
				}
				dbz := float64(v) / 10.0
				if dbz < 0 {
					dbz = 0
				} else if dbz > 75 {
					dbz = 75
				}
				vv := dbz
				rowVals[col] = &vv
			}
			vals[row] = rowVals
		}
		tiles = append(tiles, radarTileResponse{
			DT:     r.DT.In(loc).Format("2006-01-02 15:04:05"),
			Z:      r.Z,
			Y:      r.Y,
			X:      r.X,
			Width:  r.Width,
			Height: r.Height,
			West:   r.West,
			South:  r.South,
			East:   r.East,
			North:  r.North,
			ResDeg: r.ResDeg,
			Values: vals,
		})
	}

	if len(tiles) == 0 {
		c.JSON(http.StatusNotFound, gin.H{"error": "未找到指定时间的雷达瓦片集合"})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"dt":    dt.In(loc).Format("2006-01-02 15:04:05"),
		"tiles": tiles,
	})
}

// nearestRadarTileHandler 返回最接近给定时间的瓦片（支持 z/y/x、容差分钟、偏好 lte 或 nearest）
func nearestRadarTileHandler(c *gin.Context) {
	z := parseIntDefault(c.Query("z"), 7)
	y := parseIntDefault(c.Query("y"), 40)
	x := parseIntDefault(c.Query("x"), 102)
	dtStr := c.Query("dt")
	if dtStr == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "缺少 dt 参数（YYYY-MM-DD HH:MM:SS）"})
		return
	}
	tolMin := parseIntDefault(c.Query("tolerance_min"), 30)
	prefer := c.DefaultQuery("prefer", "nearest") // nearest|lte

	loc, _ := time.LoadLocation("Asia/Shanghai")
	if loc == nil {
		loc = time.FixedZone("CST", 8*3600)
	}
	target, err := time.ParseInLocation("2006-01-02 15:04:05", dtStr, loc)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "无效的 dt 格式"})
		return
	}
	from := target.Add(-time.Duration(tolMin) * time.Minute)
	to := target.Add(time.Duration(tolMin) * time.Minute)

	db := database.GetDB()
	var row *sql.Row
	if prefer == "lte" {
		const q = `
            SELECT dt FROM radar_tiles
            WHERE z=$1 AND y=$2 AND x=$3 AND dt BETWEEN $4 AND $5 AND dt <= $6
            ORDER BY ($6 - dt) ASC
            LIMIT 1`
		row = db.QueryRow(q, z, y, x, from, to, target)
	} else {
		const q = `
            SELECT dt FROM radar_tiles
            WHERE z=$1 AND y=$2 AND x=$3 AND dt BETWEEN $4 AND $5
            ORDER BY ABS(EXTRACT(EPOCH FROM (dt - $6))) ASC
            LIMIT 1`
		row = db.QueryRow(q, z, y, x, from, to, target)
	}
	var picked time.Time
	if err := row.Scan(&picked); err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "在容差范围内未找到匹配瓦片"})
		return
	}
	rec, err := getRadarTileAt(db, z, y, x, picked)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "未找到匹配瓦片数据"})
		return
	}

	// 解码与 latest/at 相同
	w, h := rec.Width, rec.Height
	if len(rec.Data) < w*h*2 {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "数据长度异常"})
		return
	}
	vals := make([][]*float64, h)
	off := 0
	for rowi := 0; rowi < h; rowi++ {
		rowVals := make([]*float64, w)
		for col := 0; col < w; col++ {
			v := int16(binary.BigEndian.Uint16(rec.Data[off : off+2]))
			off += 2
			if v >= 32766 {
				rowVals[col] = nil
				continue
			}
			dbz := float64(v) / 10.0
			if dbz < 0 {
				dbz = 0
			} else if dbz > 75 {
				dbz = 75
			}
			vv := dbz
			rowVals[col] = &vv
		}
		vals[rowi] = rowVals
	}
	// 统一以 CST(+8) 输出
	resp := radarTileResponse{
		DT:     rec.DT.In(loc).Format("2006-01-02 15:04:05"),
		Z:      rec.Z,
		Y:      rec.Y,
		X:      rec.X,
		Width:  rec.Width,
		Height: rec.Height,
		West:   rec.West,
		South:  rec.South,
		East:   rec.East,
		North:  rec.North,
		ResDeg: rec.ResDeg,
		Values: vals,
	}
	c.JSON(http.StatusOK, resp)
}

func parseIntDefault(s string, def int) int {
	if s == "" {
		return def
	}
	var n int
	_, err := fmtSscanf(s, &n)
	if err != nil || n == 0 || n == math.MinInt || n == math.MaxInt {
		return def
	}
	return n
}

// fmtSscanf is a tiny wrapper to avoid importing fmt only for Sscanf
func fmtSscanf(s string, n *int) (int, error) {
	// naive fast parse
	sign := 1
	i := 0
	if len(s) > 0 && (s[0] == '-' || s[0] == '+') {
		if s[0] == '-' {
			sign = -1
		}
		i = 1
	}
	val := 0
	for ; i < len(s); i++ {
		ch := s[i]
		if ch < '0' || ch > '9' {
			return 0, fmtError("invalid")
		}
		val = val*10 + int(ch-'0')
	}
	*n = sign * val
	return 1, nil
}

type fmtError string

func (e fmtError) Error() string { return string(e) }

// ---------------- Radar station realtime API ----------------

type radarWeatherRecord struct {
	Alias         string
	Lat           float64
	Lon           float64
	DT            time.Time
	Temperature   float64
	Humidity      float64
	Cloudrate     float64
	Visibility    float64
	Dswrf         float64
	WindSpeed     float64
	WindDirection float64
	Pressure      float64
}

type radarWeatherResponse struct {
	Alias         string  `json:"alias"`
	Lat           float64 `json:"lat"`
	Lon           float64 `json:"lon"`
	DT            string  `json:"dt"`
	Temperature   float64 `json:"temperature"`
	Humidity      float64 `json:"humidity"`
	Cloudrate     float64 `json:"cloudrate"`
	Visibility    float64 `json:"visibility"`
	Dswrf         float64 `json:"dswrf"`
	WindSpeed     float64 `json:"wind_speed"`
	WindDirection float64 `json:"wind_direction"`
	Pressure      float64 `json:"pressure"`
}

func latestRadarWeatherHandler(c *gin.Context) {
	alias := c.Query("alias")
	if alias == "" {
		alias = "南宁雷达站"
	}
	const q = `
        SELECT alias, lat, lon, dt,
               temperature, humidity, cloudrate, visibility, dswrf,
               wind_speed, wind_direction, pressure
        FROM radar_weather
        WHERE alias = $1
        ORDER BY dt DESC
        LIMIT 1`
	var r radarWeatherRecord
	err := database.GetDB().QueryRow(q, alias).Scan(
		&r.Alias, &r.Lat, &r.Lon, &r.DT,
		&r.Temperature, &r.Humidity, &r.Cloudrate, &r.Visibility, &r.Dswrf,
		&r.WindSpeed, &r.WindDirection, &r.Pressure,
	)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "未找到实时气象数据"})
		return
	}
	resp := radarWeatherResponse{
		Alias:         r.Alias,
		Lat:           r.Lat,
		Lon:           r.Lon,
		DT:            r.DT.Format("2006-01-02 15:04:05"),
		Temperature:   r.Temperature,
		Humidity:      r.Humidity,
		Cloudrate:     r.Cloudrate,
		Visibility:    r.Visibility,
		Dswrf:         r.Dswrf,
		WindSpeed:     r.WindSpeed,
		WindDirection: r.WindDirection,
		Pressure:      r.Pressure,
	}
	c.JSON(http.StatusOK, resp)
}

// radarWeatherAtHandler returns the radar weather record at the given dt (CST),
// rounded/exact 10-minute bucket time string "YYYY-MM-DD HH:MM:SS".
func radarWeatherAtHandler(c *gin.Context) {
	alias := c.Query("alias")
	if alias == "" {
		alias = "南宁雷达站"
	}
	dtStr := c.Query("dt")
	if dtStr == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "缺少 dt 参数（YYYY-MM-DD HH:MM:SS）"})
		return
	}
	loc, _ := time.LoadLocation("Asia/Shanghai")
	if loc == nil {
		loc = time.FixedZone("CST", 8*3600)
	}
	dt, err := time.ParseInLocation("2006-01-02 15:04:05", dtStr, loc)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "无效的 dt 格式"})
		return
	}
	const q = `
        SELECT alias, lat, lon, dt,
               temperature, humidity, cloudrate, visibility, dswrf,
               wind_speed, wind_direction, pressure
        FROM radar_weather
        WHERE alias = $1 AND dt = $2
        LIMIT 1`
	var r radarWeatherRecord
	err = database.GetDB().QueryRow(q, alias, dt).Scan(
		&r.Alias, &r.Lat, &r.Lon, &r.DT,
		&r.Temperature, &r.Humidity, &r.Cloudrate, &r.Visibility, &r.Dswrf,
		&r.WindSpeed, &r.WindDirection, &r.Pressure,
	)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "未找到指定时间的实时气象数据"})
		return
	}
	resp := radarWeatherResponse{
		Alias:         r.Alias,
		Lat:           r.Lat,
		Lon:           r.Lon,
		DT:            r.DT.Format("2006-01-02 15:04:05"),
		Temperature:   r.Temperature,
		Humidity:      r.Humidity,
		Cloudrate:     r.Cloudrate,
		Visibility:    r.Visibility,
		Dswrf:         r.Dswrf,
		WindSpeed:     r.WindSpeed,
		WindDirection: r.WindDirection,
		Pressure:      r.Pressure,
	}
	c.JSON(http.StatusOK, resp)
}

// radarWeatherNearestHandler returns the nearest radar_weather record to the given dt.
// prefer=lte will pick the latest record not later than dt; else chooses absolute nearest.
func radarWeatherNearestHandler(c *gin.Context) {
	alias := c.Query("alias")
	if alias == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "缺少 alias 参数"})
		return
	}
	dtStr := c.Query("dt")
	if dtStr == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "缺少 dt 参数（YYYY-MM-DD HH:MM:SS）"})
		return
	}
	prefer := c.DefaultQuery("prefer", "lte") // lte|nearest
	loc, _ := time.LoadLocation("Asia/Shanghai")
	if loc == nil {
		loc = time.FixedZone("CST", 8*3600)
	}
	target, err := time.ParseInLocation("2006-01-02 15:04:05", dtStr, loc)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "无效的 dt 格式"})
		return
	}

	var row *sql.Row
	db := database.GetDB()
	if prefer == "nearest" {
		const q = `
            SELECT alias, lat, lon, dt,
                   temperature, humidity, cloudrate, visibility, dswrf,
                   wind_speed, wind_direction, pressure
            FROM radar_weather
            WHERE alias = $1
            ORDER BY ABS(EXTRACT(EPOCH FROM (dt - $2))) ASC
            LIMIT 1`
		row = db.QueryRow(q, alias, target)
	} else { // lte default
		const q = `
            SELECT alias, lat, lon, dt,
                   temperature, humidity, cloudrate, visibility, dswrf,
                   wind_speed, wind_direction, pressure
            FROM radar_weather
            WHERE alias = $1 AND dt <= $2
            ORDER BY dt DESC
            LIMIT 1`
		row = db.QueryRow(q, alias, target)
	}

	var r radarWeatherRecord
	if err := row.Scan(
		&r.Alias, &r.Lat, &r.Lon, &r.DT,
		&r.Temperature, &r.Humidity, &r.Cloudrate, &r.Visibility, &r.Dswrf,
		&r.WindSpeed, &r.WindDirection, &r.Pressure,
	); err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "未找到就近的实时气象数据"})
		return
	}
	ageMin := int(target.Sub(r.DT).Minutes())
	if ageMin < 0 { // for nearest mode, could be future relative to target
		ageMin = -ageMin
	}
	resp := gin.H{
		"alias":          r.Alias,
		"lat":            r.Lat,
		"lon":            r.Lon,
		"dt":             r.DT.Format("2006-01-02 15:04:05"),
		"temperature":    r.Temperature,
		"humidity":       r.Humidity,
		"cloudrate":      r.Cloudrate,
		"visibility":     r.Visibility,
		"dswrf":          r.Dswrf,
		"wind_speed":     r.WindSpeed,
		"wind_direction": r.WindDirection,
		"pressure":       r.Pressure,
		"age_minutes":    ageMin,
		"stale":          ageMin > 120,
	}
	c.JSON(http.StatusOK, resp)
}

// radarWeatherAliasesHandler 返回 radar_weather 中存在的站点别名及经纬度（按最近记录去重）
func radarWeatherAliasesHandler(c *gin.Context) {
	const q = `
        SELECT DISTINCT ON (alias) alias, lat, lon, dt
        FROM radar_weather
        ORDER BY alias, dt DESC`
	rows, err := database.GetDB().Query(q)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "查询别名失败"})
		return
	}
	defer rows.Close()
	type item struct {
		Alias string  `json:"alias"`
		Lat   float64 `json:"lat"`
		Lon   float64 `json:"lon"`
	}
	var list []item
	for rows.Next() {
		var a string
		var lat, lon float64
		var dt time.Time
		if err := rows.Scan(&a, &lat, &lon, &dt); err != nil {
			continue
		}
		list = append(list, item{Alias: a, Lat: lat, Lon: lon})
	}
	c.JSON(http.StatusOK, gin.H{"aliases": list})
}

// radarConfigAliasesHandler 返回配置文件中的雷达别名列表（含 z/y/x 和经纬度）
func radarConfigAliasesHandler(c *gin.Context) {
	cfg := config.GetConfig()
	type item struct {
		Alias string  `json:"alias"`
		Lat   float64 `json:"lat"`
		Lon   float64 `json:"lon"`
		Z     int     `json:"z"`
		Y     int     `json:"y"`
		X     int     `json:"x"`
	}
	out := make([]item, 0, len(cfg.Radar.Aliases))
	for _, a := range cfg.Radar.Aliases {
		out = append(out, item{Alias: a.Alias, Lat: a.Lat, Lon: a.Lon, Z: a.Z, Y: a.Y, X: a.X})
	}
	c.JSON(http.StatusOK, gin.H{"aliases": out})
}
