package provider

import (
	"bytes"
	"fmt"
	"html/template"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"

	"github.com/gin-gonic/gin"

	"github.com/tidbcloud/AgentTeam/internal/api"
	"github.com/tidbcloud/AgentTeam/internal/api/dto"
)

const (
	githubRegistrationReturnTargetWeb     = "web"
	githubRegistrationReturnTargetDesktop = "desktop"
	githubRegistrationStartTTL            = 15 * time.Minute
)

var githubRegistrationStartPageTemplate = template.Must(template.New("github-registration-start").Parse(`<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; base-uri 'none'; form-action {{.ActionOrigin}}; script-src 'unsafe-inline'; style-src 'unsafe-inline'">
<meta name="referrer" content="no-referrer">
<title>AgentTeam GitHub App Setup</title>
</head>
<body>
<form id="github-app-manifest" method="POST" action="{{.ActionURL}}">
<input type="hidden" name="manifest" value="{{.Manifest}}">
<button type="submit">Continue to GitHub</button>
</form>
<script>document.getElementById("github-app-manifest").submit()</script>
</body>
</html>`))

var githubRegistrationDesktopCompletionPageTemplate = template.Must(template.New("github-registration-complete").Parse(`<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; base-uri 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline'; navigate-to agentteam:">
<meta name="referrer" content="no-referrer">
<title>AgentTeam GitHub App Setup Complete</title>
</head>
<body>
<p>GitHub setup is complete. Return to AgentTeam Desktop to finish.</p>
<p><a href="{{.DeepLinkHref}}">Open AgentTeam Desktop</a></p>
<script>window.location.href = {{.DeepLinkJS}}</script>
</body>
</html>`))

type githubRegistrationStartPageData struct {
	ActionOrigin string
	ActionURL    string
	Manifest     string
}

type githubRegistrationDesktopCompletionData struct {
	DeepLinkHref template.URL
	DeepLinkJS   template.JS
}

// RegistrationStart handles POST /api/v1/workspaces/:workspaceId/providers/github/registration/start.
// It returns a short-lived AgentTeam HTTPS start URL for the server-side GitHub App
// manifest flow without creating any database-backed continuation record.
func (handler *GitHubHandler) RegistrationStart(r api.WorkspaceScopedJSON[dto.GitHubRegistrationStartRequest]) (api.Response, error) {
	returnTarget := strings.TrimSpace(r.Body.ReturnTarget)
	if !validGitHubRegistrationReturnTarget(returnTarget) {
		return api.Response{}, api.NewHTTPError(http.StatusBadRequest, "GitHub setup return target is invalid")
	}
	setupMode, err := handler.githubAppSetupModeForUser(r.Context(), r.UserID(), r.WorkspaceID())
	if err != nil {
		return api.Response{}, err
	}
	state, err := handler.newGitHubFlowState("register", r.UserID(), r.WorkspaceID(), setupMode)
	if err != nil {
		return api.Response{}, api.NewHTTPError(http.StatusInternalServerError, "Failed to prepare GitHub registration")
	}
	state.ReturnTarget = returnTarget
	state.ExpiresAt = time.Now().UTC().Add(githubRegistrationStartTTL).Unix()
	token, err := handler.encodeGitHubFlowState(state)
	if err != nil {
		return api.Response{}, api.NewHTTPError(http.StatusInternalServerError, "Failed to prepare GitHub registration")
	}
	startURL := strings.TrimRight(handler.publicBaseURL, "/") + "/api/github/setup/start?token=" + url.QueryEscape(token)
	return githubNoStoreOK(dto.GitHubRegistrationStartResponse{
		StartURL:  startURL,
		ExpiresAt: time.Unix(state.ExpiresAt, 0).UTC(),
	}), nil
}

// handleGitHubRegistrationSetupStart serves GET /api/github/setup/start.
// It validates the signed start token, sets the callback cookie in the system
// browser, and serves the page that posts the GitHub App manifest to GitHub.
func handleGitHubRegistrationSetupStart(handler *GitHubHandler) gin.HandlerFunc {
	return func(ctx *gin.Context) {
		state, err := handler.consumeGitHubRegistrationStartToken(ctx.Query("token"))
		if err != nil {
			api.Error(ctx, http.StatusBadRequest, "Invalid GitHub setup state")
			return
		}
		setupMode, err := handler.githubAppSetupModeForUser(ctx.Request.Context(), state.UserID, state.WorkspaceID)
		if err != nil {
			api.StoreError(ctx, err)
			return
		}
		if setupMode != strings.TrimSpace(state.Mode) {
			api.Error(ctx, http.StatusConflict, "GitHub setup permissions changed. Restart setup.")
			return
		}
		if err := handler.issueGitHubFlowCookie(ctx, githubRegisterCookieName, state); err != nil {
			api.Error(ctx, http.StatusInternalServerError, "Failed to prepare GitHub setup")
			return
		}
		manifest, err := handler.buildGitHubManifestJSON()
		if err != nil {
			api.Error(ctx, http.StatusInternalServerError, "Failed to build GitHub App manifest")
			return
		}
		formAction := strings.TrimRight(handler.githubConfig.BaseURL, "/") + "/settings/apps/new?state=" + url.QueryEscape(state.Nonce)
		page, err := renderGitHubRegistrationStartPage(formAction, manifest)
		if err != nil {
			api.Error(ctx, http.StatusInternalServerError, "Failed to prepare GitHub setup")
			return
		}
		ctx.Header("Cache-Control", "no-store")
		ctx.Data(http.StatusOK, "text/html; charset=utf-8", page)
	}
}

func (handler *GitHubHandler) consumeGitHubRegistrationStartToken(rawToken string) (githubFlowState, error) {
	state, err := handler.decodeGitHubFlowState(strings.TrimSpace(rawToken))
	if err != nil {
		return githubFlowState{}, err
	}
	if err := validateGitHubFlowState(state, "register"); err != nil {
		return githubFlowState{}, err
	}
	if !validGitHubRegistrationReturnTarget(state.ReturnTarget) {
		return githubFlowState{}, fmt.Errorf("invalid github setup return target")
	}
	return state, nil
}

func renderGitHubRegistrationStartPage(formAction string, manifest string) ([]byte, error) {
	parsed, err := url.Parse(strings.TrimSpace(formAction))
	if err != nil || parsed.Scheme != "https" || strings.TrimSpace(parsed.Host) == "" {
		return nil, fmt.Errorf("invalid GitHub manifest form action")
	}
	var buffer bytes.Buffer
	if err := githubRegistrationStartPageTemplate.Execute(&buffer, githubRegistrationStartPageData{
		ActionOrigin: parsed.Scheme + "://" + parsed.Host,
		ActionURL:    parsed.String(),
		Manifest:     manifest,
	}); err != nil {
		return nil, fmt.Errorf("render GitHub setup page: %w", err)
	}
	return buffer.Bytes(), nil
}

func (handler *GitHubHandler) githubRegistrationDesktopDeepLink(workspaceID string, result string) string {
	values := url.Values{}
	values.Set("workspaceId", strings.TrimSpace(workspaceID))
	values.Set("result", strings.TrimSpace(result))
	return "agentteam://github-setup-complete?" + values.Encode()
}

func renderGitHubRegistrationDesktopCompletionPage(deepLink string) ([]byte, error) {
	parsed, err := url.Parse(strings.TrimSpace(deepLink))
	if err != nil || parsed.Scheme != "agentteam" || parsed.Host != "github-setup-complete" || parsed.User != nil || parsed.Port() != "" {
		return nil, fmt.Errorf("invalid GitHub setup deep link")
	}
	result := parsed.Query().Get("result")
	if result != "registered" && result != "ready" {
		return nil, fmt.Errorf("invalid GitHub setup result")
	}
	var buffer bytes.Buffer
	if err := githubRegistrationDesktopCompletionPageTemplate.Execute(&buffer, githubRegistrationDesktopCompletionData{
		DeepLinkHref: template.URL(deepLink),
		DeepLinkJS:   template.JS(strconv.Quote(deepLink)),
	}); err != nil {
		return nil, fmt.Errorf("render GitHub setup completion page: %w", err)
	}
	return buffer.Bytes(), nil
}

func validGitHubRegistrationReturnTarget(target string) bool {
	switch strings.TrimSpace(target) {
	case githubRegistrationReturnTargetWeb, githubRegistrationReturnTargetDesktop:
		return true
	default:
		return false
	}
}
