package dev

import (
	"encoding/json"
	"errors"
	"os"
	"os/exec"
	"strings"
	"time"

	"github.com/host-uk/core/pkg/cli"
	"github.com/host-uk/core/pkg/i18n"
	"github.com/host-uk/core/pkg/io"
	"github.com/host-uk/core/pkg/repos"
)

// CI-specific styles (aliases to shared)
var (
	ciSuccessStyle = cli.SuccessStyle
	ciFailureStyle = cli.ErrorStyle
	ciPendingStyle = cli.WarningStyle
	ciSkippedStyle = cli.DimStyle
)

// WorkflowRun represents a GitHub Actions workflow run
type WorkflowRun struct {
	Name       string    `json:"name"`
	Status     string    `json:"status"`
	Conclusion string    `json:"conclusion"`
	HeadBranch string    `json:"headBranch"`
	CreatedAt  time.Time `json:"createdAt"`
	UpdatedAt  time.Time `json:"updatedAt"`
	URL        string    `json:"url"`

	// Added by us
	RepoName string `json:"-"`
}

// CI command flags
var (
	ciRegistryPath string
	ciBranch       string
	ciFailedOnly   bool
)

// addCICommand adds the 'ci' command to the given parent command.
func addCICommand(parent *cli.Command) {
	ciCmd := &cli.Command{
		Use:   "ci",
		Short: i18n.T("cmd.dev.ci.short"),
		Long:  i18n.T("cmd.dev.ci.long"),
		RunE: func(cmd *cli.Command, args []string) error {
			branch := ciBranch
			if branch == "" {
				branch = "main"
			}
			return runCI(ciRegistryPath, branch, ciFailedOnly)
		},
	}

	ciCmd.Flags().StringVar(&ciRegistryPath, "registry", "", i18n.T("common.flag.registry"))
	ciCmd.Flags().StringVarP(&ciBranch, "branch", "b", "main", i18n.T("cmd.dev.ci.flag.branch"))
	ciCmd.Flags().BoolVar(&ciFailedOnly, "failed", false, i18n.T("cmd.dev.ci.flag.failed"))

	parent.AddCommand(ciCmd)
}

func runCI(registryPath string, branch string, failedOnly bool) error {
	// Check gh is available
	if _, err := exec.LookPath("gh"); err != nil {
		return errors.New(i18n.T("error.gh_not_found"))
	}

	// Find or use provided registry
	var reg *repos.Registry
	var err error

	if registryPath != "" {
		reg, err = repos.LoadRegistry(io.Local, registryPath)
		if err != nil {
			return cli.Wrap(err, "failed to load registry")
		}
	} else {
		registryPath, err = repos.FindRegistry(io.Local)
		if err == nil {
			reg, err = repos.LoadRegistry(io.Local, registryPath)
			if err != nil {
				return cli.Wrap(err, "failed to load registry")
			}
		} else {
			cwd, _ := os.Getwd()
			reg, err = repos.ScanDirectory(io.Local, cwd)
			if err != nil {
				return cli.Wrap(err, "failed to scan directory")
			}
		}
	}

	// Fetch CI status sequentially
	var allRuns []WorkflowRun
	var fetchErrors []error
	var noCI []string

	repoList := reg.List()
	for i, repo := range repoList {
		repoFullName := cli.Sprintf("%s/%s", reg.Org, repo.Name)
		cli.Print("\033[2K\r%s %d/%d %s", dimStyle.Render(i18n.T("i18n.progress.check")), i+1, len(repoList), repo.Name)

		runs, err := fetchWorkflowRuns(repoFullName, repo.Name, branch)
		if err != nil {
			if strings.Contains(err.Error(), "no workflows") {
				noCI = append(noCI, repo.Name)
			} else {
				fetchErrors = append(fetchErrors, cli.Wrap(err, repo.Name))
			}
			continue
		}

		if len(runs) > 0 {
			// Just get the latest run
			allRuns = append(allRuns, runs[0])
		} else {
			noCI = append(noCI, repo.Name)
		}
	}
	cli.Print("\033[2K\r") // Clear progress line

	// Count by status
	var success, failed, pending, other int
	for _, run := range allRuns {
		switch run.Conclusion {
		case "success":
			success++
		case "failure":
			failed++
		case "":
			if run.Status == "in_progress" || run.Status == "queued" {
				pending++
			} else {
				other++
			}
		default:
			other++
		}
	}

	// Print summary
	cli.Blank()
	cli.Print("%s", i18n.T("cmd.dev.ci.repos_checked", map[string]interface{}{"Count": len(repoList)}))
	if success > 0 {
		cli.Print(" * %s", ciSuccessStyle.Render(i18n.T("cmd.dev.ci.passing", map[string]interface{}{"Count": success})))
	}
	if failed > 0 {
		cli.Print(" * %s", ciFailureStyle.Render(i18n.T("cmd.dev.ci.failing", map[string]interface{}{"Count": failed})))
	}
	if pending > 0 {
		cli.Print(" * %s", ciPendingStyle.Render(i18n.T("common.count.pending", map[string]interface{}{"Count": pending})))
	}
	if len(noCI) > 0 {
		cli.Print(" * %s", ciSkippedStyle.Render(i18n.T("cmd.dev.ci.no_ci", map[string]interface{}{"Count": len(noCI)})))
	}
	cli.Blank()
	cli.Blank()

	// Filter if needed
	displayRuns := allRuns
	if failedOnly {
		displayRuns = nil
		for _, run := range allRuns {
			if run.Conclusion == "failure" {
				displayRuns = append(displayRuns, run)
			}
		}
	}

	// Print details
	for _, run := range displayRuns {
		printWorkflowRun(run)
	}

	// Print errors
	if len(fetchErrors) > 0 {
		cli.Blank()
		for _, err := range fetchErrors {
			cli.Print("%s %s\n", errorStyle.Render(i18n.Label("error")), err)
		}
	}

	return nil
}

func fetchWorkflowRuns(repoFullName, repoName string, branch string) ([]WorkflowRun, error) {
	args := []string{
		"run", "list",
		"--repo", repoFullName,
		"--branch", branch,
		"--limit", "1",
		"--json", "name,status,conclusion,headBranch,createdAt,updatedAt,url",
	}

	cmd := exec.Command("gh", args...)
	output, err := cmd.Output()
	if err != nil {
		if exitErr, ok := err.(*exec.ExitError); ok {
			stderr := string(exitErr.Stderr)
			return nil, cli.Err("%s", strings.TrimSpace(stderr))
		}
		return nil, err
	}

	var runs []WorkflowRun
	if err := json.Unmarshal(output, &runs); err != nil {
		return nil, err
	}

	// Tag with repo name
	for i := range runs {
		runs[i].RepoName = repoName
	}

	return runs, nil
}

func printWorkflowRun(run WorkflowRun) {
	// Status icon
	var status string
	switch run.Conclusion {
	case "success":
		status = ciSuccessStyle.Render("v")
	case "failure":
		status = ciFailureStyle.Render("x")
	case "":
		switch run.Status {
		case "in_progress":
			status = ciPendingStyle.Render("*")
		case "queued":
			status = ciPendingStyle.Render("o")
		default:
			status = ciSkippedStyle.Render("-")
		}
	case "skipped":
		status = ciSkippedStyle.Render("-")
	case "cancelled":
		status = ciSkippedStyle.Render("o")
	default:
		status = ciSkippedStyle.Render("?")
	}

	// Workflow name (truncated)
	workflowName := cli.Truncate(run.Name, 20)

	// Age
	age := cli.FormatAge(run.UpdatedAt)

	cli.Print("  %s %-18s %-22s %s\n",
		status,
		repoNameStyle.Render(run.RepoName),
		dimStyle.Render(workflowName),
		issueAgeStyle.Render(age),
	)
}
