package repo

import (
	"encoding/csv"
	"fmt"
	"os"
	"strings"

	"github.com/gitlink-org/gitlink-cli/shortcuts/common"
)

func newBatchUpdateShortcut() *common.Shortcut {
	return &common.Shortcut{
		Name:        "batch-update",
		Description: "Update settings for multiple repositories",
		Flags: []common.Flag{
			{Name: "names", Short: "n", Usage: "Comma-separated repository names, e.g. repo-a,repo-b"},
			{Name: "from", Usage: "CSV file path"},
			{Name: "description", Short: "d", Usage: "Shared description for all repos"},
			{Name: "private", Usage: "Set repos to private", Bool: true, Default: "false"},
			{Name: "public", Usage: "Set repos to public", Bool: true, Default: "false"},
			{Name: "dry-run", Usage: "Preview without making changes", Bool: true, Default: "false"},
		},
		Run: runBatchUpdate,
	}
}

func runBatchUpdate(ctx *common.RuntimeContext) error {
	if err := ctx.ResolveOwnerRepo(); err != nil {
		return err
	}

	var repoNames []string
	if namesStr := ctx.Arg("names"); namesStr != "" {
		for _, name := range strings.Split(namesStr, ",") {
			name = strings.TrimSpace(name)
			if name != "" {
				repoNames = append(repoNames, name)
			}
		}
	}
	if csvPath := ctx.Arg("from"); csvPath != "" {
		csvNames, err := readNamesFromCSV(csvPath)
		if err != nil {
			return err
		}
		repoNames = append(repoNames, csvNames...)
	}
	if len(repoNames) == 0 {
		return fmt.Errorf("no repository names provided; use -n repo-a,repo-b or --from repos.csv")
	}

	setPrivate := ctx.Arg("private") == "true"
	setPublic := ctx.Arg("public") == "true"
	if setPrivate && setPublic {
		return fmt.Errorf("cannot use both --private and --public")
	}
	changeVisibility := setPrivate || setPublic

	dryRun := ctx.Arg("dry-run") == "true"
	desc := ctx.Arg("description")

	summary := repoBatchSummary{
		Owner:   ctx.Owner,
		Action:  "update",
		DryRun:  dryRun,
		Total:   len(repoNames),
		Results: make([]repoBatchResult, 0, len(repoNames)),
	}

	for _, name := range repoNames {
		result := repoBatchResult{Name: name}
		if dryRun {
			result.Status = "planned"
			summary.Succeeded++
			summary.Results = append(summary.Results, result)
			continue
		}

		// Fetch current repo info to get required fields for PATCH
		current, err := ctx.CallAPI("GET", fmt.Sprintf("/%s/%s", ctx.Owner, name), nil)
		if err != nil {
			result.Status = "failed"
			result.Error = fmt.Sprintf("fetch repo: %v", err)
			summary.Failed++
			summary.Results = append(summary.Results, result)
			continue
		}

		curData, _ := current.Data.(map[string]interface{})
		curName, _ := curData["name"].(string)
		identifier, _ := curData["identifier"].(string)

		body := map[string]interface{}{
			"name":       curName,
			"identifier": identifier,
		}
		if desc != "" {
			body["description"] = desc
		}
		if changeVisibility {
			body["private"] = setPrivate
		}

		if _, err := ctx.CallAPI("PATCH", fmt.Sprintf("/%s/%s", ctx.Owner, name), body); err != nil {
			result.Status = "failed"
			result.Error = err.Error()
			summary.Failed++
		} else {
			result.Status = "updated"
			summary.Succeeded++
		}
		summary.Results = append(summary.Results, result)
	}

	if err := ctx.OutputData(summary); err != nil {
		return err
	}
	if summary.Failed > 0 {
		return fmt.Errorf("%d of %d repo(s) failed to update", summary.Failed, summary.Total)
	}
	return nil
}

func readNamesFromCSV(path string) ([]string, error) {
	file, err := os.Open(path)
	if err != nil {
		return nil, fmt.Errorf("read CSV: %w", err)
	}
	defer file.Close()

	reader := csv.NewReader(file)
	reader.TrimLeadingSpace = true
	records, err := reader.ReadAll()
	if err != nil {
		return nil, fmt.Errorf("parse CSV: %w", err)
	}
	if len(records) < 2 {
		return nil, fmt.Errorf("CSV must have a header row and at least one data row")
	}

	header := records[0]
	nameCol := -1
	for i, h := range header {
		if strings.ToLower(strings.TrimSpace(h)) == "name" {
			nameCol = i
			break
		}
	}
	if nameCol == -1 {
		nameCol = 0
	}

	var names []string
	for _, record := range records[1:] {
		if nameCol >= len(record) {
			continue
		}
		name := strings.TrimSpace(record[nameCol])
		if name != "" {
			names = append(names, name)
		}
	}
	return names, nil
}
