package cmd

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"os/exec"
	pathpkg "path"
	"path/filepath"
	"strings"

	"github.com/innerr/ticat/pkg/core/model"

	"github.com/tidbcloud/controlplane9/internal/cluster"
	"github.com/tidbcloud/controlplane9/internal/config"
	"github.com/tidbcloud/controlplane9/internal/dbaasui"
	cp9errors "github.com/tidbcloud/controlplane9/internal/errors"
	"github.com/tidbcloud/controlplane9/internal/host"
	cptilt "github.com/tidbcloud/controlplane9/internal/tilt"
	"github.com/tidbcloud/controlplane9/internal/verbose"
)

const (
	essentialV2StatusEvidenceAPIVersion = "cp9.essential-v2.status/v1alpha1"
	essentialV2StatusEvidenceKind       = "EssentialV2Status"
	essentialV2StatusEvidenceFileName   = "essential-v2-status.json"
	essentialV2ModeActivate             = essentialV2Mode("activate")
	essentialV2ModeStatus               = essentialV2Mode("status")
)

type essentialV2Mode string

type essentialV2RuntimeStatus struct {
	State         string `json:"state"`
	SharedPoolID  string `json:"shared_pool_id,omitempty"`
	DataplaneName string `json:"dataplane_name,omitempty"`
	Phase         string `json:"phase,omitempty"`
	Version       string `json:"version,omitempty"`
	Message       string `json:"message,omitempty"`
}

type essentialV2StatusResult struct {
	State          string         `json:"state"`
	Session        string         `json:"session"`
	Persona        string         `json:"persona"`
	StateDir       string         `json:"state_dir"`
	OrganizationID string         `json:"organization_id"`
	ProjectID      string         `json:"project_id"`
	RegionID       string         `json:"region_id"`
	ServicePlan    string         `json:"service_plan"`
	SharedPoolID   string         `json:"shared_pool_id,omitempty"`
	DataplaneName  string         `json:"dataplane_name,omitempty"`
	Phase          string         `json:"phase,omitempty"`
	Version        string         `json:"version,omitempty"`
	Message        string         `json:"message"`
	Activation     *activationRef `json:"activation,omitempty"`
}

type essentialV2StatusEvidence struct {
	APIVersion string                   `json:"apiVersion"`
	Kind       string                   `json:"kind"`
	Result     *essentialV2StatusResult `json:"result"`
}

type essentialV2ScriptInput struct {
	Mode            essentialV2Mode
	StateDir        string
	ScriptPath      string
	Kubeconfig      string
	KubeContext     string
	ContextPath     string
	StatusPath      string
	LocalStatusPath string
	Remote          *host.Session
}

type essentialV2ScriptRunnerFunc func(context.Context, *essentialV2ScriptInput) (*essentialV2RuntimeStatus, error)

var essentialV2ScriptRunner essentialV2ScriptRunnerFunc = runEssentialV2Script

func (r *essentialV2StatusResult) String() string {
	if r == nil {
		return "essential-v2 status: <nil>"
	}
	var b strings.Builder
	fmt.Fprintf(&b, "essential-v2 %s: %s", r.State, r.Message)
	fmt.Fprintf(&b, "\nidentity: organization=%s project=%s region=%s service-plan=%s",
		r.OrganizationID, r.ProjectID, r.RegionID, r.ServicePlan)
	if r.SharedPoolID != "" {
		fmt.Fprintf(&b, "\nshared pool: %s", r.SharedPoolID)
	}
	if r.DataplaneName != "" {
		fmt.Fprintf(&b, "\ndataplane: %s", r.DataplaneName)
	}
	if r.Phase != "" {
		fmt.Fprintf(&b, "\nphase: %s", r.Phase)
	}
	if r.Version != "" {
		fmt.Fprintf(&b, "\nversion: %s", r.Version)
	}
	return b.String()
}

func essentialV2ActivateCmd() model.NormalCmd {
	return func(args model.ArgVals, cc *model.Cli, env *model.Env, _ []model.ParsedCmd) error {
		syncVerbose(env)
		result, err := runEssentialV2Activate(context.Background(), args.GetRaw("config"))
		if result != nil {
			if outputErr := model.Output(cc, env, result); outputErr != nil {
				return outputErr
			}
		}
		return err
	}
}

func essentialV2StatusCmd() model.NormalCmd {
	return func(args model.ArgVals, cc *model.Cli, env *model.Env, _ []model.ParsedCmd) error {
		syncVerbose(env)
		result, err := runEssentialV2Status(context.Background(), args.GetRaw("config"))
		if err != nil {
			return err
		}
		return model.Output(cc, env, result)
	}
}

func runEssentialV2Activate(ctx context.Context, cfgPath string) (*essentialV2StatusResult, error) {
	result, err := runEssentialV2(ctx, cfgPath, essentialV2ModeActivate)
	if err != nil {
		return result, err
	}
	if result.State != "ready" {
		return result, cp9errors.Newf(cp9errors.CategoryResource, "Essential V2 activation is not ready: %s", result.Message)
	}
	return result, nil
}

func runEssentialV2Status(ctx context.Context, cfgPath string) (*essentialV2StatusResult, error) {
	return runEssentialV2(ctx, cfgPath, essentialV2ModeStatus)
}

func runEssentialV2(ctx context.Context, cfgPath string, mode essentialV2Mode) (result *essentialV2StatusResult, err error) {
	resolved, selected, err := resolveDevFullSession(cfgPath, "cp9 essential-v2")
	if err != nil {
		return nil, err
	}
	result = &essentialV2StatusResult{
		State:          "not_ready",
		Session:        resolved.Name,
		Persona:        config.SessionPersona(resolved),
		StateDir:       selected.StateDir,
		OrganizationID: config.EssentialV2UIOrganizationID,
		ProjectID:      config.EssentialV2UIProjectID,
		RegionID:       resolved.Target.Cloud + "-" + resolved.Target.Region,
		ServicePlan:    "Essential_V2",
		Message:        "Dev full baseline is not ready; run cp9 up first",
		Activation:     &activationRef{Command: "cp9 essential-v2 activate"},
	}
	defer func() {
		if err == nil {
			err = writeEssentialV2StatusEvidence(result)
		}
	}()
	if selected.Mode == "" || strings.TrimSpace(selected.KubeconfigPath) == "" || strings.TrimSpace(selected.Context) == "" {
		return result, nil
	}
	if mode == essentialV2ModeActivate {
		feature := dbaasui.EnsureEssentialV2UIFeature(ctx, func(ctx context.Context, input []byte, args ...string) ([]byte, error) {
			return host.RunKubectl(ctx, selected, input, args...)
		})
		if !feature.Ready {
			result.Message = feature.Message
			return result, cp9errors.Newf(cp9errors.CategoryResource, "enable Essential V2 UI plan: %s", feature.Message)
		}
	}

	repoRoot, err := sessionRepoRoot(resolved)
	if err != nil {
		return nil, err
	}
	statusPath := essentialV2RuntimeStatusPath(selected.StateDir)
	input := essentialV2ScriptInput{
		Mode:        mode,
		StateDir:    selected.StateDir,
		ScriptPath:  filepath.Join(repoRoot, "personas", "dev-full", "runtime", "scripts", "bootstrap-essential-v2-shared-pool.sh"),
		Kubeconfig:  selected.KubeconfigPath,
		KubeContext: selected.Context,
		ContextPath: filepath.Join(cluster.ContextDir(selected.StateDir), "context.json"),
		StatusPath:  statusPath,
	}
	if mode == essentialV2ModeActivate && selected.IsRemote() {
		state, loadErr := cluster.LoadState(selected.StateDir)
		if loadErr != nil {
			return nil, fmt.Errorf("load lifecycle state for remote Essential V2 activation: %w", loadErr)
		}
		tiltStatus, loadErr := cptilt.LoadStatusForState(selected.StateDir, state)
		if loadErr != nil {
			return nil, fmt.Errorf("load remote Tilt controller status for Essential V2 activation: %w", loadErr)
		}
		if _, loadErr := cptilt.RefreshRemoteControllerInputs(ctx, selected, selected.StateDir, tiltStatus); loadErr != nil {
			return nil, fmt.Errorf("refresh remote controller inputs for Essential V2 activation: %w", loadErr)
		}
		remoteRepoRoot, loadErr := cptilt.RemoteControllerRepositoryRoot(tiltStatus)
		if loadErr != nil {
			return nil, loadErr
		}
		input.Remote = selected
		input.ScriptPath = pathpkg.Join(remoteRepoRoot, "personas", "dev-full", "runtime", "scripts", "bootstrap-essential-v2-shared-pool.sh")
		input.Kubeconfig = tiltStatus.RemoteKubeconfigPath
		input.ContextPath = tiltStatus.RemoteContextIndexPath
		input.LocalStatusPath = statusPath
		input.StatusPath = pathpkg.Join(tiltStatus.RemoteRoot, "session", "reports", "evidence", filepath.Base(statusPath))
	}
	runtimeStatus, err := essentialV2ScriptRunner(ctx, &input)
	if err != nil {
		return result, fmt.Errorf("essential-v2 %s: %w", mode, err)
	}
	result.State = runtimeStatus.State
	result.SharedPoolID = runtimeStatus.SharedPoolID
	result.DataplaneName = runtimeStatus.DataplaneName
	result.Phase = runtimeStatus.Phase
	result.Version = runtimeStatus.Version
	result.Message = runtimeStatus.Message
	if result.State == "ready" {
		result.Activation = nil
	}
	return result, nil
}

func runEssentialV2Script(ctx context.Context, input *essentialV2ScriptInput) (*essentialV2RuntimeStatus, error) {
	if input == nil {
		return nil, errors.New("essential V2 script input is required")
	}
	if input.Mode != essentialV2ModeActivate && input.Mode != essentialV2ModeStatus {
		return nil, fmt.Errorf("unsupported Essential V2 script mode %q", input.Mode)
	}
	localStatusPath := input.StatusPath
	if input.LocalStatusPath != "" {
		localStatusPath = input.LocalStatusPath
	}
	if err := os.MkdirAll(filepath.Dir(localStatusPath), 0o755); err != nil {
		return nil, fmt.Errorf("create Essential V2 status dir: %w", err)
	}
	var data []byte
	if input.Remote != nil {
		if _, err := host.RunSSH(ctx, input.Remote, nil, "rm", "-f", input.StatusPath); err != nil {
			return nil, fmt.Errorf("clear remote Essential V2 runtime status: %w", err)
		}
		output, runErr := host.RunSSH(
			ctx,
			input.Remote,
			nil,
			"env",
			"CP9_ESSENTIAL_V2_MODE="+string(input.Mode),
			"CP9_ESSENTIAL_V2_STATUS_FILE="+input.StatusPath,
			"bash",
			input.ScriptPath,
			input.Kubeconfig,
			input.KubeContext,
			input.ContextPath,
		)
		var statusErr error
		data, statusErr = host.RunSSH(ctx, input.Remote, nil, "cat", input.StatusPath)
		if statusErr != nil {
			statusErr = fmt.Errorf("read remote Essential V2 runtime status: %w", statusErr)
		} else {
			if err := os.WriteFile(localStatusPath, data, 0o600); err != nil {
				statusErr = fmt.Errorf("persist Essential V2 runtime status: %w", err)
			}
		}
		if runErr != nil {
			runErr = fmt.Errorf("run remote shared-pool bootstrap: %s", trimmedCommandError("command", output, runErr))
		}
		if err := errors.Join(runErr, statusErr); err != nil {
			return nil, err
		}
	} else {
		//nolint:gosec // Script path is derived from the resolved, repository-owned Session Tiltfile.
		command := exec.CommandContext(
			ctx,
			"bash",
			input.ScriptPath,
			input.Kubeconfig,
			input.KubeContext,
			input.ContextPath,
		)
		command.Env = append(os.Environ(),
			"CP9_ESSENTIAL_V2_MODE="+string(input.Mode),
			"CP9_ESSENTIAL_V2_STATUS_FILE="+input.StatusPath,
		)
		output, err := verbose.RunCommand(command)
		if err != nil {
			return nil, fmt.Errorf("run shared-pool bootstrap: %s", trimmedCommandError("command", output, err))
		}
		data, err = os.ReadFile(input.StatusPath)
		if err != nil {
			return nil, fmt.Errorf("read Essential V2 runtime status: %w", err)
		}
	}
	var status essentialV2RuntimeStatus
	if err := json.Unmarshal(data, &status); err != nil {
		return nil, fmt.Errorf("parse Essential V2 runtime status: %w", err)
	}
	return &status, nil
}

func essentialV2RuntimeStatusPath(stateDir string) string {
	return filepath.Join(stateDir, "reports", "evidence", "essential-v2-runtime-status.json")
}

func essentialV2StatusEvidencePath(stateDir string) string {
	return filepath.Join(stateDir, "reports", "evidence", essentialV2StatusEvidenceFileName)
}

func writeEssentialV2StatusEvidence(result *essentialV2StatusResult) error {
	if result == nil || strings.TrimSpace(result.StateDir) == "" {
		return nil
	}
	path := essentialV2StatusEvidencePath(result.StateDir)
	if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
		return fmt.Errorf("create Essential V2 status evidence dir: %w", err)
	}
	data, err := json.MarshalIndent(essentialV2StatusEvidence{
		APIVersion: essentialV2StatusEvidenceAPIVersion,
		Kind:       essentialV2StatusEvidenceKind,
		Result:     result,
	}, "", "  ")
	if err != nil {
		return fmt.Errorf("marshal Essential V2 status evidence: %w", err)
	}
	data = append(data, '\n')
	if err := os.WriteFile(path, data, 0o600); err != nil {
		return fmt.Errorf("write Essential V2 status evidence: %w", err)
	}
	return nil
}
