package admin

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

func (h *Handler) syncVercel(w http.ResponseWriter, r *http.Request) {
	var req map[string]any
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]any{"detail": "invalid json"})
		return
	}
	vercelToken, _ := req["vercel_token"].(string)
	projectID, _ := req["project_id"].(string)
	teamID, _ := req["team_id"].(string)
	autoValidate := true
	if v, ok := req["auto_validate"].(bool); ok {
		autoValidate = v
	}
	saveCreds := true
	if v, ok := req["save_credentials"].(bool); ok {
		saveCreds = v
	}
	usePreconfig := vercelToken == "__USE_PRECONFIG__" || strings.TrimSpace(vercelToken) == ""
	if usePreconfig {
		vercelToken = strings.TrimSpace(os.Getenv("VERCEL_TOKEN"))
	}
	if strings.TrimSpace(projectID) == "" {
		projectID = strings.TrimSpace(os.Getenv("VERCEL_PROJECT_ID"))
	}
	if strings.TrimSpace(teamID) == "" {
		teamID = strings.TrimSpace(os.Getenv("VERCEL_TEAM_ID"))
	}
	if vercelToken == "" || projectID == "" {
		writeJSON(w, http.StatusBadRequest, map[string]any{"detail": "需要 Vercel Token 和 Project ID"})
		return
	}
	validated, failed := 0, []string{}
	if autoValidate {
		for _, acc := range h.Store.Snapshot().Accounts {
			if strings.TrimSpace(acc.Token) != "" {
				continue
			}
			token, err := h.DS.Login(r.Context(), acc)
			if err != nil {
				failed = append(failed, acc.Identifier())
			} else {
				validated++
				_ = h.Store.UpdateAccountToken(acc.Identifier(), token)
			}
			time.Sleep(500 * time.Millisecond)
		}
	}

	cfgJSON, _, err := h.Store.ExportJSONAndBase64()
	if err != nil {
		writeJSON(w, http.StatusInternalServerError, map[string]any{"detail": err.Error()})
		return
	}
	cfgB64 := base64.StdEncoding.EncodeToString([]byte(cfgJSON))
	client := &http.Client{Timeout: 30 * time.Second}
	params := url.Values{}
	if teamID != "" {
		params.Set("teamId", teamID)
	}
	headers := map[string]string{"Authorization": "Bearer " + vercelToken}
	envResp, status, err := vercelRequest(r.Context(), client, http.MethodGet, "https://api.vercel.com/v9/projects/"+projectID+"/env", params, headers, nil)
	if err != nil || status != http.StatusOK {
		writeJSON(w, statusOr(status, http.StatusInternalServerError), map[string]any{"detail": "获取环境变量失败"})
		return
	}
	envs, _ := envResp["envs"].([]any)
	existingEnvID := findEnvID(envs, "DS2API_CONFIG_JSON")
	if existingEnvID != "" {
		_, status, err = vercelRequest(r.Context(), client, http.MethodPatch, "https://api.vercel.com/v9/projects/"+projectID+"/env/"+existingEnvID, params, headers, map[string]any{"value": cfgB64})
	} else {
		_, status, err = vercelRequest(r.Context(), client, http.MethodPost, "https://api.vercel.com/v10/projects/"+projectID+"/env", params, headers, map[string]any{"key": "DS2API_CONFIG_JSON", "value": cfgB64, "type": "encrypted", "target": []string{"production", "preview"}})
	}
	if err != nil || (status != http.StatusOK && status != http.StatusCreated) {
		writeJSON(w, statusOr(status, http.StatusInternalServerError), map[string]any{"detail": "更新环境变量失败"})
		return
	}
	savedCreds := []string{}
	if saveCreds && !usePreconfig {
		creds := [][2]string{{"VERCEL_TOKEN", vercelToken}, {"VERCEL_PROJECT_ID", projectID}}
		if teamID != "" {
			creds = append(creds, [2]string{"VERCEL_TEAM_ID", teamID})
		}
		for _, kv := range creds {
			id := findEnvID(envs, kv[0])
			if id != "" {
				_, status, _ = vercelRequest(r.Context(), client, http.MethodPatch, "https://api.vercel.com/v9/projects/"+projectID+"/env/"+id, params, headers, map[string]any{"value": kv[1]})
			} else {
				_, status, _ = vercelRequest(r.Context(), client, http.MethodPost, "https://api.vercel.com/v10/projects/"+projectID+"/env", params, headers, map[string]any{"key": kv[0], "value": kv[1], "type": "encrypted", "target": []string{"production", "preview"}})
			}
			if status == http.StatusOK || status == http.StatusCreated {
				savedCreds = append(savedCreds, kv[0])
			}
		}
	}
	projectResp, status, _ := vercelRequest(r.Context(), client, http.MethodGet, "https://api.vercel.com/v9/projects/"+projectID, params, headers, nil)
	manual := true
	deployURL := ""
	if status == http.StatusOK {
		if link, ok := projectResp["link"].(map[string]any); ok {
			if linkType, _ := link["type"].(string); linkType == "github" {
				repoID := intFrom(link["repoId"])
				ref, _ := link["productionBranch"].(string)
				if ref == "" {
					ref = "main"
				}
				depResp, depStatus, _ := vercelRequest(r.Context(), client, http.MethodPost, "https://api.vercel.com/v13/deployments", params, headers, map[string]any{"name": projectID, "project": projectID, "target": "production", "gitSource": map[string]any{"type": "github", "repoId": repoID, "ref": ref}})
				if depStatus == http.StatusOK || depStatus == http.StatusCreated {
					deployURL, _ = depResp["url"].(string)
					manual = false
				}
			}
		}
	}
	_ = h.Store.SetVercelSync(h.computeSyncHash(), time.Now().Unix())
	result := map[string]any{"success": true, "validated_accounts": validated}
	if manual {
		result["message"] = "配置已同步到 Vercel，请手动触发重新部署"
		result["manual_deploy_required"] = true
	} else {
		result["message"] = "配置已同步，正在重新部署..."
		result["deployment_url"] = deployURL
	}
	if len(failed) > 0 {
		result["failed_accounts"] = failed
	}
	if len(savedCreds) > 0 {
		result["saved_credentials"] = savedCreds
	}
	writeJSON(w, http.StatusOK, result)
}

func (h *Handler) vercelStatus(w http.ResponseWriter, _ *http.Request) {
	snap := h.Store.Snapshot()
	current := h.computeSyncHash()
	synced := snap.VercelSyncHash != "" && snap.VercelSyncHash == current
	writeJSON(w, http.StatusOK, map[string]any{"synced": synced, "last_sync_time": nilIfZero(snap.VercelSyncTime), "has_synced_before": snap.VercelSyncHash != ""})
}

func vercelRequest(ctx context.Context, client *http.Client, method, endpoint string, params url.Values, headers map[string]string, body any) (map[string]any, int, error) {
	if len(params) > 0 {
		endpoint += "?" + params.Encode()
	}
	var reader io.Reader
	if body != nil {
		b, _ := json.Marshal(body)
		reader = bytes.NewReader(b)
	}
	req, err := http.NewRequestWithContext(ctx, method, endpoint, reader)
	if err != nil {
		return nil, 0, err
	}
	for k, v := range headers {
		req.Header.Set(k, v)
	}
	req.Header.Set("Content-Type", "application/json")
	resp, err := client.Do(req)
	if err != nil {
		return nil, 0, err
	}
	defer resp.Body.Close()
	b, _ := io.ReadAll(resp.Body)
	parsed := map[string]any{}
	_ = json.Unmarshal(b, &parsed)
	if len(parsed) == 0 {
		parsed["raw"] = string(b)
	}
	return parsed, resp.StatusCode, nil
}

func findEnvID(envs []any, key string) string {
	for _, item := range envs {
		m, ok := item.(map[string]any)
		if !ok {
			continue
		}
		if k, _ := m["key"].(string); k == key {
			id, _ := m["id"].(string)
			return id
		}
	}
	return ""
}
