package server

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

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

type rainTileRecord 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 rainTileResponse 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"` // 单位：mm，null 表示无效
}

func getLatestRainTile(db *sql.DB, z, y, x int) (*rainTileRecord, error) {
	const q = `
        SELECT dt, z, y, x, width, height, west, south, east, north, res_deg, data
        FROM rain_tiles
        WHERE z=$1 AND y=$2 AND x=$3
        ORDER BY dt DESC
        LIMIT 1`
	var r rainTileRecord
	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 getRainTileAt(db *sql.DB, z, y, x int, dt time.Time) (*rainTileRecord, error) {
	const q = `
        SELECT dt, z, y, x, width, height, west, south, east, north, res_deg, data
        FROM rain_tiles
        WHERE z=$1 AND y=$2 AND x=$3 AND dt=$4
        LIMIT 1`
	var r rainTileRecord
	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
}

// latestRainTileHandler 返回指定 z/y/x 的最新一小时降雨瓦片
func latestRainTileHandler(c *gin.Context) {
	z := parseIntDefault(c.Query("z"), 7)
	y := parseIntDefault(c.Query("y"), 40)
	x := parseIntDefault(c.Query("x"), 102)

	rec, err := getLatestRainTile(database.GetDB(), z, y, x)
	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 := decodeRain(rec.Data, w, h)
	loc, _ := time.LoadLocation("Asia/Shanghai")
	if loc == nil {
		loc = time.FixedZone("CST", 8*3600)
	}
	resp := rainTileResponse{
		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)
}

// rainTileAtHandler 返回指定 z/y/x 的指定时间（CST）瓦片
func rainTileAtHandler(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 := getRainTileAt(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 := decodeRain(rec.Data, w, h)
	// 始终以 CST(+8) 输出
	resp := rainTileResponse{
		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)
}

// rainTileTimesHandler 返回指定 z/y/x 的可用时间列表（倒序）
func rainTileTimesHandler(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 rain_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 rain_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})
}

// nearestRainTileHandler 返回最接近给定时间的瓦片
func nearestRainTileHandler(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"), 90)
	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
	}
	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 rain_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 rain_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 := getRainTileAt(db, z, y, x, picked)
	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 := decodeRain(rec.Data, w, h)
	// 以 CST(+8) 输出
	resp := rainTileResponse{
		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)
}

// rainTilesAtHandler 返回指定 z 在 dt 时次的全部雨量瓦片（不同 y/x）集合
// GET /api/rain/tiles_at?z=7&dt=YYYY-MM-DD HH:MM:SS
func rainTilesAtHandler(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
	}

	const q = `
        SELECT dt, z, y, x, width, height, west, south, east, north, res_deg, data
        FROM rain_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 []rainTileResponse
	for rows.Next() {
		var r rainTileRecord
		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 := decodeRain(r.Data, w, h)
		tiles = append(tiles, rainTileResponse{
			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,
	})
}

func decodeRain(buf []byte, w, h int) [][]*float64 {
	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(buf[off : off+2]))
			off += 2
			if v >= 32766 { // 无效
				rowVals[col] = nil
				continue
			}
			mm := float64(v) / 10.0 // 0.1 mm 精度
			if mm < 0 {
				mm = 0
			}
			vv := mm
			rowVals[col] = &vv
		}
		vals[row] = rowVals
	}
	return vals
}
