package main

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"time"
)

// Server Configuration Port
const PORT = "8099"

// Configuration mapped from config.json
type Config struct {
	DifyKey      string `json:"dify_key"`
	DifyUrl      string `json:"dify_url"`
	DeepSeekKey  string `json:"deepseek_key"`
	GeminiKey    string `json:"gemini_key"`
	Proxy        string `json:"proxy"`
}

var globalConfig Config

// Payload definitions
type BaziData struct {
	GenderTxt string `json:"genderTxt"`
	BaziStr   string `json:"baziStr"`
	WuxingStr string `json:"wuxingStr"`
	Xishen    string `json:"xishen"`
}

type NamingParams struct {
	LastName  string   `json:"lastName"`
	Gender    string   `json:"gender"`
	BirthDate string   `json:"birthDate"`
	Wuxing    []string `json:"wuxing"`
	Bazi      BaziData `json:"bazi"`
}

func main() {
	loadConfig()

	// 1. Static Web Server (Hosting local files for easy testing)
	fs := http.FileServer(http.Dir("."))
	http.Handle("/", fs)

	// 2. API Gateway Route
	http.HandleFunc("/api/name", handleNamingRequest)

	fmt.Printf("☯️  灵签阁 · 高并发Go网关服务运行中: http://localhost:%s\n", PORT)
	if err := http.ListenAndServe(":"+PORT, nil); err != nil {
		fmt.Printf("Error starting server: %v\n", err)
	}
}

// Load configuration keys securely
func loadConfig() {
	execPath, err := os.Executable()
	var dir string
	if err == nil {
		dir = filepath.Dir(execPath)
	} else {
		dir = "."
	}

	configPath := filepath.Join(dir, "config.json")
	if _, err := os.Stat(configPath); os.IsNotExist(err) {
		// Fallback to current working directory
		configPath = "config.json"
	}

	file, err := os.Open(configPath)
	if err != nil {
		fmt.Printf("[CONFIG WARNING] Failed to open config.json: %v. Running in pure Local Fallback mode.\n", err)
		return
	}
	defer file.Close()

	decoder := json.NewDecoder(file)
	if err := decoder.Decode(&globalConfig); err != nil {
		fmt.Printf("[CONFIG WARNING] Failed to parse config.json: %v\n", err)
		return
	}

	// Clean Dify URL trailing slash
	globalConfig.DifyUrl = strings.TrimSuffix(globalConfig.DifyUrl, "/v1")
	fmt.Printf("[CONFIG] Keys successfully loaded. Dify URL: %s, Proxy Enabled: %v\n", globalConfig.DifyUrl, globalConfig.Proxy != "")
}

// CORS Headers Injector
func setupCORS(w *http.ResponseWriter, r *http.Request) bool {
	(*w).Header().Set("Access-Control-Allow-Origin", "*")
	(*w).Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS")
	(*w).Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
	if r.Method == "OPTIONS" {
		(*w).WriteHeader(http.StatusOK)
		return true
	}
	return false
}

// Main API Handler
func handleNamingRequest(w http.ResponseWriter, r *http.Request) {
	if setupCORS(&w, r) {
		return
	}

	if r.Method != "POST" {
		http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
		return
	}

	var params NamingParams
	body, err := io.ReadAll(r.Body)
	if err != nil {
		http.Error(w, "Bad Request", http.StatusBadRequest)
		return
	}

	if err := json.Unmarshal(body, &params); err != nil {
		http.Error(w, "Invalid JSON", http.StatusBadRequest)
		return
	}

	w.Header().Set("Content-Type", "application/json; charset=utf-8")

	// If keys are missing, automatically return empty response to trigger Frontend Soothing Local Fallback Database
	if globalConfig.DifyKey == "" || globalConfig.DeepSeekKey == "" {
		w.Write([]byte(`{"status":"local_fallback"}`))
		return
	}

	// 1. Fetch Candidates from Dify Knowledge Base (Dataset RAG)
	candidates := retrieveDifyCandidates(params)

	// 2. Format Prompts
	systemPrompt := "你是一位深谙中国姓名学、康熙笔画、周易八字数理、以及《诗经》《楚辞》出处起名的国学大师。\n" +
		"用户会给你宝宝的姓氏、性别、喜用五行以及我们从古籍知识库召回的候选汉字及诗词片段。\n" +
		"请严格按照【姓名学三才五格81数理吉凶】及【生辰五行生克】，挑选出3个字音洪亮高雅、字意吉利、有诗词出处的绝佳名字。\n" +
		"请你直接以如下结构的 JSON 格式返回，不要包含任何 markdown 代码包裹框，也不要输出多余说明文：\n" +
		"[\n" +
		"  {\n" +
		"    \"name\": \"思齐\",\n" +
		"    \"pinyin\": \"Sī Qí\",\n" +
		"    \"wuxing\": \"金木\",\n" +
		"    \"strokes\": \"9 + 14\",\n" +
		"    \"lucky\": \"大吉\",\n" +
		"    \"source\": \"《诗经·大雅·思齐》\",\n" +
		"    \"quote\": \"思齐大姒，武王之母。\",\n" +
		"    \"meaning\": \"名字释意与姓名学五行生克解释\"\n" +
		"  }\n" +
		"]"

	var userPrompt string
	if candidates != "" {
		userPrompt = fmt.Sprintf("【姓氏】: %s\n【性别】: %s\n【八字命盘】: %s\n【五行旺衰】: %s\n【喜用五行】: %s\n\n【古籍召回候选字及出处】:\n%s\n\n请严格挑选出3个大吉名字并以规定的纯JSON结构返回。",
			params.LastName, params.Gender, params.Bazi.BaziStr, params.Bazi.WuxingStr, params.Bazi.Xishen, candidates)
	} else {
		userPrompt = fmt.Sprintf("【姓氏】: %s\n【性别】: %s\n【八字命盘】: %s\n【五行旺衰】: %s\n【喜用五行】: %s\n\n请直接挑选出3个契合喜神、大吉且声律优美的国风雅致名字并以规定的纯JSON结构返回。",
			params.LastName, params.Gender, params.Bazi.BaziStr, params.Bazi.WuxingStr, params.Bazi.Xishen)
	}

	// 3. Execute Dual-Channel LLM Refinement (DeepSeek first, Gemini as failover)
	resultJSON := executeLLMRequest(systemPrompt, userPrompt)

	w.Write([]byte(resultJSON))
}

// Retrieve from Dify API
func retrieveDifyCandidates(params NamingParams) string {
	difyUrl := fmt.Sprintf("%s/v1/datasets/dataset-45ModIklUC7ZkQplk213KZQy/retrieve", globalConfig.DifyUrl)

	// Build retrieval query
	queryStr := fmt.Sprintf("姓氏 %s, 喜用五行 %s", params.LastName, params.Bazi.Xishen)
	difyPayload := map[string]interface{}{
		"query": queryStr,
	}
	payloadBytes, _ := json.Marshal(difyPayload)

	req, err := http.NewRequest("POST", difyUrl, bytes.NewBuffer(payloadBytes))
	if err != nil {
		return ""
	}
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", "Bearer "+globalConfig.DifyKey)

	client := &http.Client{Timeout: 10 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		fmt.Printf("[DIFY WARNING] RAG retrieval connection failed: %v\n", err)
		return ""
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		return ""
	}

	var resData map[string]interface{}
	json.NewDecoder(resp.Body).Decode(&resData)

	records, ok := resData["records"].([]interface{})
	if !ok || len(records) == 0 {
		return ""
	}

	var candidates []string
	for _, r := range records {
		if recMap, ok := r.(map[string]interface{}); ok {
			if segment, ok := recMap["segment"].(map[string]interface{}); ok {
				if content, ok := segment["content"].(string); ok {
					candidates = append(candidates, content)
				}
			}
		}
	}
	return strings.Join(candidates, "\n---\n")
}

// Dual Channel API Pipeline
func executeLLMRequest(systemPrompt, userPrompt string) string {
	// Channel A: DeepSeek (No proxy, direct connection)
	ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
	defer cancel()

	deepseekJSON, err := requestDeepSeek(ctx, systemPrompt, userPrompt)
	if err == nil && deepseekJSON != "" {
		return deepseekJSON
	}

	fmt.Printf("[LLM FALLBACK] DeepSeek API failed or timed out: %v. Activating Google Gemini failover...\n", err)

	// Channel B: Gemini Failover (With Proxy if configured)
	geminiCtx, geminiCancel := context.WithTimeout(context.Background(), 20*time.Second)
	defer geminiCancel()

	geminiJSON, err := requestGemini(geminiCtx, systemPrompt, userPrompt)
	if err == nil && geminiJSON != "" {
		return geminiJSON
	}

	fmt.Printf("[LLM ERROR] Both DeepSeek and Gemini channels failed: %v\n", err)
	return `{"status":"local_fallback"}`
}

func requestDeepSeek(ctx context.Context, systemPrompt, userPrompt string) (string, error) {
	apiUrl := "https://api.deepseek.com/chat/completions"
	payload := map[string]interface{}{
		"model": "deepseek-chat",
		"messages": []map[string]string{
			{"role": "system", "content": systemPrompt},
			{"role": "user", "content": userPrompt},
		},
		"response_format": map[string]string{"type": "json_object"},
		"temperature":     0.7,
	}

	payloadBytes, _ := json.Marshal(payload)
	req, err := http.NewRequestWithContext(ctx, "POST", apiUrl, bytes.NewBuffer(payloadBytes))
	if err != nil {
		return "", err
	}
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", "Bearer "+globalConfig.DeepSeekKey)

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		bodyBytes, _ := io.ReadAll(resp.Body)
		return "", fmt.Errorf("HTTP status %d: %s", resp.StatusCode, string(bodyBytes))
	}

	var resData map[string]interface{}
	json.NewDecoder(resp.Body).Decode(&resData)

	choices, ok := resData["choices"].([]interface{})
	if !ok || len(choices) == 0 {
		return "", fmt.Errorf("invalid DeepSeek response structure")
	}

	choiceMap, _ := choices[0].(map[string]interface{})
	msgMap, _ := choiceMap["message"].(map[string]interface{})
	content, _ := msgMap["content"].(string)

	return content, nil
}

func requestGemini(ctx context.Context, systemPrompt, userPrompt string) (string, error) {
	apiUrl := fmt.Sprintf("https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=%s", globalConfig.GeminiKey)

	payload := map[string]interface{}{
		"contents": []map[string]interface{}{
			{
				"parts": []map[string]string{
					{"text": systemPrompt + "\n\n用户输入内容:\n" + userPrompt},
				},
			},
		},
		"generationConfig": map[string]interface{}{
			"responseMimeType": "application/json",
			"temperature":      0.7,
		},
	}

	payloadBytes, _ := json.Marshal(payload)
	req, err := http.NewRequestWithContext(ctx, "POST", apiUrl, bytes.NewBuffer(payloadBytes))
	if err != nil {
		return "", err
	}
	req.Header.Set("Content-Type", "application/json")

	// Set SOCKS5 / HTTP Proxy for Gemini if configured
	client := &http.Client{}
	if globalConfig.Proxy != "" {
		proxyClean := globalConfig.Proxy
		if strings.HasPrefix(proxyClean, "socks5://") {
			proxyClean = strings.Replace(proxyClean, "socks5://", "http://", 1)
		}
		proxyUrl, err := url.Parse(proxyClean)
		if err == nil {
			transport := &http.Transport{Proxy: http.ProxyURL(proxyUrl)}
			client.Transport = transport
		}
	}

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

	if resp.StatusCode != 200 {
		bodyBytes, _ := io.ReadAll(resp.Body)
		return "", fmt.Errorf("HTTP status %d: %s", resp.StatusCode, string(bodyBytes))
	}

	var resData map[string]interface{}
	json.NewDecoder(resp.Body).Decode(&resData)

	candidates, ok := resData["candidates"].([]interface{})
	if !ok || len(candidates) == 0 {
		return "", fmt.Errorf("invalid Gemini response structure")
	}

	candMap, _ := candidates[0].(map[string]interface{})
	contentMap, _ := candMap["content"].(map[string]interface{})
	parts, _ := contentMap["parts"].([]interface{})
	if len(parts) == 0 {
		return "", fmt.Errorf("empty Gemini parts response")
	}

	partMap, _ := parts[0].(map[string]interface{})
	text, _ := partMap["text"].(string)

	return text, nil
}
