package cmd

import (
	"context"
	"encoding/json"
	"fmt"
	"os"
	"strings"

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

	cp9errors "github.com/tidbcloud/controlplane9/internal/errors"
	"github.com/tidbcloud/controlplane9/internal/migrate"
)

type dbDumpInitResult struct {
	OutputPath string   `json:"outputPath"`
	Database   string   `json:"database"`
	Schema     []string `json:"schemaTables"`
	Rows       []string `json:"rowTables"`
}

func (r *dbDumpInitResult) FormatText() string {
	var b strings.Builder
	fmt.Fprintf(&b, "cp9 db dump-init wrote %s\n", r.OutputPath)
	fmt.Fprintf(&b, "  database: %s\n", r.Database)
	fmt.Fprintf(&b, "  schema tables: %d\n", len(r.Schema))
	fmt.Fprintf(&b, "  row tables: %d\n", len(r.Rows))
	return b.String()
}

// dbDumpInitCmd dumps a source MySQL database into a persona-owned init SQL file.
func dbDumpInitCmd() model.NormalCmd {
	return func(args model.ArgVals, cc *model.Cli, env *model.Env, _ []model.ParsedCmd) error {
		syncVerbose(env)

		outputPath := strings.TrimSpace(args.GetRaw("output"))
		if outputPath == "" {
			return cp9errors.New(cp9errors.CategoryConfig, "output argument is required")
		}
		url := strings.TrimSpace(args.GetRaw("url"))
		urlEnv := strings.TrimSpace(args.GetRaw("url-env"))
		if url == "" && urlEnv != "" {
			url = strings.TrimSpace(os.Getenv(urlEnv))
			if url == "" {
				return cp9errors.Newf(cp9errors.CategoryConfig, "url-env %q is empty or unset", urlEnv)
			}
		}
		if url == "" {
			return cp9errors.New(cp9errors.CategoryConfig, "url or url-env argument is required")
		}
		tableWhere, err := loadDumpInitWhereFile(args.GetRaw("where-file"))
		if err != nil {
			return cp9errors.Newf(cp9errors.CategoryConfig, "where-file: %v", err)
		}

		result, err := migrate.DumpInitSQL(context.Background(), &migrate.InitDumpOptions{
			URL:            url,
			Database:       args.GetRaw("database"),
			TargetDatabase: args.GetRaw("target-database"),
			OutputPath:     outputPath,
			RowTables:      splitCSV(args.GetRaw("tables")),
			Where:          args.GetRaw("where"),
			TableWhere:     tableWhere,
			IncludeDeleted: args.GetRaw("include-deleted") == "true",
		})
		if err != nil {
			return cp9errors.Newf(cp9errors.CategoryResource, "db dump-init: %v", err)
		}

		rows := make([]string, 0, len(result.Rows))
		for _, row := range result.Rows {
			if row.Where == "" {
				rows = append(rows, row.Table)
			} else {
				rows = append(rows, row.Table+" WHERE "+row.Where)
			}
		}
		return model.Output(cc, env, &dbDumpInitResult{
			OutputPath: result.OutputPath,
			Database:   result.Database,
			Schema:     result.Schema.Tables,
			Rows:       rows,
		})
	}
}

func loadDumpInitWhereFile(path string) (map[string]string, error) {
	path = strings.TrimSpace(path)
	if path == "" {
		return map[string]string{}, nil
	}
	data, err := os.ReadFile(path)
	if err != nil {
		return nil, fmt.Errorf("read %s: %w", path, err)
	}
	var tableWhere map[string]string
	if err := json.Unmarshal(data, &tableWhere); err != nil {
		return nil, fmt.Errorf("parse %s: %w", path, err)
	}
	for table, where := range tableWhere {
		if strings.TrimSpace(table) == "" {
			return nil, fmt.Errorf("table name is required")
		}
		if strings.TrimSpace(where) == "" {
			return nil, fmt.Errorf("where clause for table %q is empty", table)
		}
	}
	return tableWhere, nil
}

func splitCSV(s string) []string {
	var parts []string
	for p := range strings.SplitSeq(s, ",") {
		p = strings.TrimSpace(p)
		if p != "" {
			parts = append(parts, p)
		}
	}
	return parts
}
