package main

import (
	"embed"
	"encoding/json"
	"fmt"
	"html/template"
	"io"
	"io/fs"
	"log"
	"math"
	"net"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"runtime"
	"strings"
	"time"

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

//go:embed templates/*
var templatesFS embed.FS

//go:embed static/*
var staticFS embed.FS

// Config 配置结构
type Config struct {
	AmapKey      string `json:"amap_key"`       // Web服务API Key
	AmapJsKey    string `json:"amap_js_key"`    // JS API Key
	AmapJsSecret string `json:"amap_js_secret"` // JS API 安全密钥
	Port         string `json:"port"`
}

// Coordinate 坐标结构
type Coordinate struct {
	Lng float64 `json:"lng"`
	Lat float64 `json:"lat"`
}

// Location 位置信息
type Location struct {
	Name       string     `json:"name"`
	Address    string     `json:"address"`
	Coordinate Coordinate `json:"coordinate"`
}

// CenterRequest 计算中心点请求
type CenterRequest struct {
	Locations []Location `json:"locations"`
}

// SearchRequest 搜索请求
type SearchRequest struct {
	Center   Coordinate `json:"center"`
	Keywords string     `json:"keywords"`
	Radius   int        `json:"radius"` // 搜索半径，单位米
}

// POI 兴趣点
type POI struct {
	ID       string     `json:"id"`
	Name     string     `json:"name"`
	Type     string     `json:"type"`
	Address  string     `json:"address"`
	Location Coordinate `json:"location"`
	Distance string     `json:"distance"`
	Tel      string     `json:"tel"`
}

// AmapGeoResponse 高德地理编码响应
type AmapGeoResponse struct {
	Status   string `json:"status"`
	Info     string `json:"info"`
	Count    string `json:"count"`
	Geocodes []struct {
		FormattedAddress string `json:"formatted_address"`
		Location         string `json:"location"`
		Level            string `json:"level"`
	} `json:"geocodes"`
}

// AmapPOI 高德POI项
type AmapPOI struct {
	ID       string      `json:"id"`
	Name     string      `json:"name"`
	Type     string      `json:"type"`
	Address  interface{} `json:"address"`  // 可能是字符串或空数组
	Location string      `json:"location"`
	Distance string      `json:"distance"`
	Tel      interface{} `json:"tel"` // 可能是字符串或空数组
}

// AmapPOIResponse 高德POI搜索响应
type AmapPOIResponse struct {
	Status     string `json:"status"`
	Info       string `json:"info"`
	Count      string `json:"count"`
	Suggestion struct {
		Keywords []interface{} `json:"keywords"`
	} `json:"suggestion"`
	Pois []AmapPOI `json:"pois"`
}

// AmapTip 高德输入提示项
type AmapTip struct {
	ID       interface{} `json:"id"`       // 可能是字符串或空数组
	Name     string      `json:"name"`
	District string      `json:"district"`
	Address  interface{} `json:"address"`  // 可能是字符串或空数组
	Location interface{} `json:"location"` // 可能是字符串或空数组
}

// AmapTipsResponse 高德输入提示响应
type AmapTipsResponse struct {
	Status string    `json:"status"`
	Info   string    `json:"info"`
	Count  string    `json:"count"`
	Tips   []AmapTip `json:"tips"`
}

var config Config

// checkAndKillPort 检查端口是否被占用，如果被占用则尝试杀死占用进程
func checkAndKillPort(port string) {
	addr := ":" + port
	ln, err := net.Listen("tcp", addr)
	if err == nil {
		// 端口未被占用，关闭监听
		ln.Close()
		return
	}

	log.Printf("端口 %s 已被占用，正在尝试释放...", port)

	// 根据操作系统执行不同的命令
	var cmd *exec.Cmd
	if runtime.GOOS == "windows" {
		// Windows: 查找并杀死占用端口的进程
		// 先查找 PID
		findCmd := exec.Command("cmd", "/c", fmt.Sprintf("netstat -ano | findstr :%s | findstr LISTENING", port))
		output, err := findCmd.Output()
		if err != nil {
			log.Printf("查找端口占用进程失败: %v", err)
			return
		}

		// 解析 PID
		lines := strings.Split(string(output), "\n")
		for _, line := range lines {
			fields := strings.Fields(line)
			if len(fields) >= 5 {
				pid := fields[len(fields)-1]
				if pid != "" && pid != "0" {
					log.Printf("发现占用进程 PID: %s，正在终止...", pid)
					killCmd := exec.Command("taskkill", "/F", "/PID", pid)
					killCmd.Run()
				}
			}
		}
	} else {
		// Linux/Mac: 使用 lsof 和 kill
		cmd = exec.Command("sh", "-c", fmt.Sprintf("lsof -ti:%s | xargs -r kill -9", port))
		cmd.Run()
	}

	// 等待端口释放
	time.Sleep(500 * time.Millisecond)
	log.Printf("端口 %s 已释放", port)
}

func main() {
	// 加载配置
	loadConfig()

	// 检查并释放端口
	port := config.Port
	if port == "" {
		port = "8080"
	}
	checkAndKillPort(port)

	// 设置Gin模式
	gin.SetMode(gin.ReleaseMode)

	r := gin.Default()

	// 从嵌入的文件系统加载模板
	tmpl := template.Must(template.New("").ParseFS(templatesFS, "templates/*.html"))
	r.SetHTMLTemplate(tmpl)

	// 从嵌入的文件系统提供静态文件
	staticSubFS, _ := fs.Sub(staticFS, "static")
	r.StaticFS("/static", http.FS(staticSubFS))

	// 路由
	r.GET("/", indexHandler)
	r.GET("/api/config", getConfigHandler)
	r.POST("/api/geocode", geocodeHandler)
	r.POST("/api/center", calculateCenterHandler)
	r.POST("/api/search", searchPOIHandler)
	r.GET("/api/tips", getTipsHandler)

	log.Printf("========================================")
	log.Printf("  会面点 Meeting Point")
	log.Printf("  服务启动在 http://localhost:%s", port)
	log.Printf("========================================")
	r.Run(":" + port)
}

func loadConfig() {
	// 尝试从配置文件加载
	file, err := os.Open("config.json")
	if err == nil {
		defer file.Close()
		decoder := json.NewDecoder(file)
		err = decoder.Decode(&config)
		if err != nil {
			log.Printf("解析配置文件失败: %v", err)
		}
	} else {
		log.Printf("警告: 未找到 config.json 配置文件，请确保配置文件存在")
	}

	// 环境变量覆盖
	if key := os.Getenv("AMAP_KEY"); key != "" {
		config.AmapKey = key
	}
	if port := os.Getenv("PORT"); port != "" {
		config.Port = port
	}

	if config.Port == "" {
		config.Port = "8080"
	}

	if config.AmapKey == "" {
		log.Printf("警告: 未配置高德地图 API Key，请在 config.json 中配置 amap_key")
	}
}

func indexHandler(c *gin.Context) {
	c.HTML(http.StatusOK, "index.html", nil)
}

func getConfigHandler(c *gin.Context) {
	// 返回前端需要的配置
	jsKey := config.AmapJsKey
	if jsKey == "" {
		jsKey = config.AmapKey // 兼容：如果没有单独配置JS Key，使用Web服务Key
	}
	c.JSON(http.StatusOK, gin.H{
		"amap_key":       config.AmapKey,
		"amap_js_key":    jsKey,
		"amap_js_secret": config.AmapJsSecret,
	})
}

// geocodeHandler 地理编码 - 地址转坐标
func geocodeHandler(c *gin.Context) {
	var req struct {
		Address string `json:"address"`
		City    string `json:"city"`
	}

	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "无效的请求参数"})
		return
	}

	if req.Address == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "地址不能为空"})
		return
	}

	// 调用高德地理编码API
	apiURL := fmt.Sprintf(
		"https://restapi.amap.com/v3/geocode/geo?key=%s&address=%s&city=%s",
		config.AmapKey,
		url.QueryEscape(req.Address),
		url.QueryEscape(req.City),
	)

	resp, err := http.Get(apiURL)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "请求高德API失败"})
		return
	}
	defer resp.Body.Close()

	body, _ := io.ReadAll(resp.Body)
	var amapResp AmapGeoResponse
	if err := json.Unmarshal(body, &amapResp); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "解析响应失败"})
		return
	}

	if amapResp.Status != "1" || len(amapResp.Geocodes) == 0 {
		c.JSON(http.StatusNotFound, gin.H{"error": "未找到该地址"})
		return
	}

	// 解析坐标
	location := amapResp.Geocodes[0].Location
	coords := strings.Split(location, ",")
	if len(coords) != 2 {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "坐标格式错误"})
		return
	}

	var lng, lat float64
	fmt.Sscanf(coords[0], "%f", &lng)
	fmt.Sscanf(coords[1], "%f", &lat)

	c.JSON(http.StatusOK, gin.H{
		"location": Location{
			Name:    req.Address,
			Address: amapResp.Geocodes[0].FormattedAddress,
			Coordinate: Coordinate{
				Lng: lng,
				Lat: lat,
			},
		},
	})
}

// calculateCenterHandler 计算多点中心
func calculateCenterHandler(c *gin.Context) {
	var req CenterRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "无效的请求参数"})
		return
	}

	if len(req.Locations) < 2 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "至少需要2个位置点"})
		return
	}

	// 计算几何中心（加权平均）
	center := calculateGeometricCenter(req.Locations)

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

// calculateGeometricCenter 计算几何中心点
func calculateGeometricCenter(locations []Location) Coordinate {
	// 使用球面坐标转换计算更准确的中心点
	var x, y, z float64

	for _, loc := range locations {
		// 转换为弧度
		latRad := loc.Coordinate.Lat * math.Pi / 180
		lngRad := loc.Coordinate.Lng * math.Pi / 180

		// 转换为笛卡尔坐标
		x += math.Cos(latRad) * math.Cos(lngRad)
		y += math.Cos(latRad) * math.Sin(lngRad)
		z += math.Sin(latRad)
	}

	// 平均值
	n := float64(len(locations))
	x /= n
	y /= n
	z /= n

	// 转换回经纬度
	lng := math.Atan2(y, x) * 180 / math.Pi
	hyp := math.Sqrt(x*x + y*y)
	lat := math.Atan2(z, hyp) * 180 / math.Pi

	return Coordinate{
		Lng: lng,
		Lat: lat,
	}
}

// searchPOIHandler 搜索周边POI
func searchPOIHandler(c *gin.Context) {
	var req SearchRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "无效的请求参数"})
		return
	}

	if req.Keywords == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "搜索关键词不能为空"})
		return
	}

	if req.Radius <= 0 {
		req.Radius = 3000 // 默认3公里
	}

	// 调用高德POI搜索API
	location := fmt.Sprintf("%f,%f", req.Center.Lng, req.Center.Lat)
	apiURL := fmt.Sprintf(
		"https://restapi.amap.com/v3/place/around?key=%s&location=%s&keywords=%s&radius=%d&offset=20&page=1&extensions=all",
		config.AmapKey,
		location,
		url.QueryEscape(req.Keywords),
		req.Radius,
	)

	resp, err := http.Get(apiURL)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "请求高德API失败"})
		return
	}
	defer resp.Body.Close()

	body, _ := io.ReadAll(resp.Body)

	var amapResp AmapPOIResponse
	if err := json.Unmarshal(body, &amapResp); err != nil {
		log.Printf("解析POI响应失败: %v", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "解析响应失败"})
		return
	}

	if amapResp.Status != "1" {
		log.Printf("高德POI搜索API返回错误: status=%s, info=%s", amapResp.Status, amapResp.Info)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "高德API返回错误: " + amapResp.Info})
		return
	}

	// 转换POI数据
	pois := make([]POI, 0, len(amapResp.Pois))
	for _, p := range amapResp.Pois {
		coords := strings.Split(p.Location, ",")
		if len(coords) != 2 {
			continue
		}
		var lng, lat float64
		fmt.Sscanf(coords[0], "%f", &lng)
		fmt.Sscanf(coords[1], "%f", &lat)

		// 处理可能是数组或字符串的字段
		address, _ := p.Address.(string)
		tel, _ := p.Tel.(string)

		pois = append(pois, POI{
			ID:      p.ID,
			Name:    p.Name,
			Type:    p.Type,
			Address: address,
			Location: Coordinate{
				Lng: lng,
				Lat: lat,
			},
			Distance: p.Distance,
			Tel:      tel,
		})
	}

	c.JSON(http.StatusOK, gin.H{
		"pois":  pois,
		"count": len(pois),
	})
}

// getTipsHandler 输入提示
func getTipsHandler(c *gin.Context) {
	keywords := c.Query("keywords")
	city := c.Query("city")

	if keywords == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "关键词不能为空"})
		return
	}

	apiURL := fmt.Sprintf(
		"https://restapi.amap.com/v3/assistant/inputtips?key=%s&keywords=%s&city=%s&datatype=all",
		config.AmapKey,
		url.QueryEscape(keywords),
		url.QueryEscape(city),
	)

	resp, err := http.Get(apiURL)
	if err != nil {
		log.Printf("请求高德API失败: %v", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "请求高德API失败"})
		return
	}
	defer resp.Body.Close()

	body, _ := io.ReadAll(resp.Body)
	var amapResp AmapTipsResponse
	if err := json.Unmarshal(body, &amapResp); err != nil {
		log.Printf("解析响应失败: %v, body: %s", err, string(body))
		c.JSON(http.StatusInternalServerError, gin.H{"error": "解析响应失败"})
		return
	}

	if amapResp.Status != "1" {
		log.Printf("高德API返回错误: status=%s, info=%s", amapResp.Status, amapResp.Info)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "高德API返回错误: " + amapResp.Info})
		return
	}

	// 过滤有效的提示
	tips := make([]map[string]interface{}, 0)
	for _, tip := range amapResp.Tips {
		// 解析 location 字段（可能是字符串或空数组）
		locationStr, ok := tip.Location.(string)
		if !ok || locationStr == "" {
			continue // 跳过非字符串类型的 location
		}

		coords := strings.Split(locationStr, ",")
		if len(coords) != 2 {
			continue
		}
		var lng, lat float64
		fmt.Sscanf(coords[0], "%f", &lng)
		fmt.Sscanf(coords[1], "%f", &lat)

		// 解析 address 字段（可能是字符串或数组）
		addressStr, _ := tip.Address.(string)

		// 解析 id 字段（可能是字符串或数组）
		idStr, _ := tip.ID.(string)

		tips = append(tips, map[string]interface{}{
			"id":       idStr,
			"name":     tip.Name,
			"district": tip.District,
			"address":  addressStr,
			"location": Coordinate{Lng: lng, Lat: lat},
		})
	}

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