package repo

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

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

type repoCreateInput struct {
	Name        string
	Description string
	Private     bool
}

type repoBatchResult struct {
	Name   string `json:"name" yaml:"name"`
	Status string `json:"status" yaml:"status"`
	Error  string `json:"error,omitempty" yaml:"error,omitempty"`
}

type repoBatchSummary struct {
	Owner     string            `json:"owner" yaml:"owner"`
	Action    string            `json:"action" yaml:"action"`
	DryRun    bool              `json:"dry_run" yaml:"dry_run"`
	Total     int               `json:"total" yaml:"total"`
	Succeeded int               `json:"succeeded" yaml:"succeeded"`
	Failed    int               `json:"failed" yaml:"failed"`
	Results   []repoBatchResult `json:"results" yaml:"results"`
}

func newBatchCreateShortcut() *common.Shortcut {
	return &common.Shortcut{
		Name:        "batch-create",
		Description: "Create multiple repositories from CLI flags or a CSV file",
		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 (inline mode)"},
			{Name: "private", Usage: "Make repos private", Bool: true, Default: "false"},
			{Name: "dry-run", Usage: "Preview without creating", Bool: true, Default: "false"},
		},
		Run: runBatchCreate,
	}
}

func runBatchCreate(ctx *common.RuntimeContext) error {
	var inputs []repoCreateInput

	if namesStr := ctx.Arg("names"); namesStr != "" {
		for _, name := range strings.Split(namesStr, ",") {
			name = strings.TrimSpace(name)
			if name == "" {
				continue
			}
			inputs = append(inputs, repoCreateInput{
				Name:        name,
				Description: ctx.Arg("description"),
				Private:     ctx.Arg("private") == "true",
			})
		}
	}

	if csvPath := ctx.Arg("from"); csvPath != "" {
		csvInputs, err := readRepoInputsFromCSV(csvPath)
		if err != nil {
			return err
		}
		inputs = append(inputs, csvInputs...)
	}

	if len(inputs) == 0 {
		return fmt.Errorf("no repository names provided; use -n repo-a,repo-b or --from repos.csv")
	}

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

	var login string
	var userID int
	if !dryRun {
		userEnv, err := ctx.CallAPI("GET", "/users/me", nil)
		if err != nil {
			return fmt.Errorf("failed to get current user: %w", err)
		}
		userData, _ := userEnv.Data.(map[string]interface{})
		login, _ = userData["login"].(string)
		if login == "" {
			return fmt.Errorf("cannot determine current user login")
		}
		if uid, ok := userData["user_id"].(float64); ok {
			userID = int(uid)
		}
	}

	summary := repoBatchSummary{
		Owner:   login,
		Action:  "create",
		DryRun:  dryRun,
		Total:   len(inputs),
		Results: make([]repoBatchResult, 0, len(inputs)),
	}

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

		body := map[string]interface{}{
			"name":            input.Name,
			"repository_name": input.Name,
			"user_id":         userID,
		}
		if input.Description != "" {
			body["description"] = input.Description
		}
		if input.Private {
			body["private"] = true
		}

		if _, err := ctx.CallAPI("POST", fmt.Sprintf("/%s/%s", login, input.Name), body); err != nil {
			result.Status = "failed"
			result.Error = err.Error()
			summary.Failed++
		} else {
			result.Status = "created"
			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 create", summary.Failed, summary.Total)
	}
	return nil
}

func readRepoInputsFromCSV(path string) ([]repoCreateInput, 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]
	col := make(map[string]int)
	for i, h := range header {
		col[strings.ToLower(strings.TrimSpace(h))] = i
	}
	if _, ok := col["name"]; !ok {
		return nil, fmt.Errorf("CSV must have a 'name' column")
	}

	var inputs []repoCreateInput
	for _, record := range records[1:] {
		name := getCol(record, col, "name")
		if name == "" {
			continue
		}
		private := false
		if p := strings.ToLower(getCol(record, col, "private")); p == "true" || p == "1" {
			private = true
		}
		inputs = append(inputs, repoCreateInput{
			Name:        name,
			Description: getCol(record, col, "description"),
			Private:     private,
		})
	}
	return inputs, nil
}

func getCol(record []string, col map[string]int, name string) string {
	if idx, ok := col[name]; ok && idx < len(record) {
		return strings.TrimSpace(record[idx])
	}
	return ""
}
