package main

import (
	"bufio"
	"encoding/json"
	"flag"
	"fmt"
	"io/fs"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"unicode"

	"gopkg.in/yaml.v3"
)

const (
	statusGreen          = "\U0001F7E2"
	statusYellow         = "\U0001F7E1"
	statusRed            = "\U0001F534"
	statusNoCoverage     = "\u2B55"
	statusNotImplemented = "\U0001F6AB"
)

var (
	statusOrder  = []string{statusGreen, statusYellow, statusRed, statusNoCoverage, statusNotImplemented}
	serviceOrder = []string{
		"rest_api",
		"ui_api",
		"metadata_api",
		"analytics_api",
		"bulk_api_v2",
		"tooling_api",
		"connect_api",
		"streaming_api",
	}
	issueByService = map[string]issueRef{
		"rest_api":      {Number: 222, URL: "https://github.com/pingcap-inc/MetaDrivenPlatform4AI/issues/222"},
		"ui_api":        {Number: 223, URL: "https://github.com/pingcap-inc/MetaDrivenPlatform4AI/issues/223"},
		"metadata_api":  {Number: 335, URL: "https://github.com/pingcap-inc/MetaDrivenPlatform4AI/issues/335"},
		"analytics_api": {Number: 336, URL: "https://github.com/pingcap-inc/MetaDrivenPlatform4AI/issues/336"},
		"bulk_api_v2":   {Number: 337, URL: "https://github.com/pingcap-inc/MetaDrivenPlatform4AI/issues/337"},
		"tooling_api":   {Number: 338, URL: "https://github.com/pingcap-inc/MetaDrivenPlatform4AI/issues/338"},
		"connect_api":   {Number: 339, URL: "https://github.com/pingcap-inc/MetaDrivenPlatform4AI/issues/339"},
		"streaming_api": {Number: 340, URL: "https://github.com/pingcap-inc/MetaDrivenPlatform4AI/issues/340"},
	}
)

type issueRef struct {
	Number int    `json:"number"`
	URL    string `json:"url"`
}

type matrixSelection struct {
	Service []string `json:"service"`
}

type catalogFile struct {
	APIService string            `yaml:"api_service"`
	Categories []catalogCategory `yaml:"categories"`
}

type catalogCategory struct {
	Endpoints []catalogEndpoint `yaml:"endpoints"`
}

type catalogEndpoint struct {
	Name     string `yaml:"name"`
	Method   string `yaml:"method"`
	Path     string `yaml:"path"`
	Coverage string `yaml:"coverage"`
	TestFile string `yaml:"test_file"`
}

type endpointStatus struct {
	Name     string `json:"name"`
	Method   string `json:"method"`
	Path     string `json:"path"`
	Coverage string `json:"coverage"`
	TestFile string `json:"test_file,omitempty"`
	Status   string `json:"status"`
}

type reportRow struct {
	PathCell string
	Status   string
}

type goTestEvent struct {
	Action  string `json:"Action"`
	Package string `json:"Package"`
	Test    string `json:"Test"`
}

type packageRunSummary struct {
	PackageStatuses map[string]string `json:"-"`
}

type serviceSummary struct {
	ServiceDir string           `json:"service_dir"`
	Module     string           `json:"module"`
	Issue      issueRef         `json:"issue"`
	Total      int              `json:"total"`
	Counts     map[string]int   `json:"counts"`
	PassRate   string           `json:"pass_rate"`
	Endpoints  []endpointStatus `json:"endpoints,omitempty"`
}

func main() {
	var artifactsDir string
	var outputPath string
	var jsonOutputPath string
	var servicesJSON string

	flag.StringVar(&artifactsDir, "artifacts", "", "directory containing downloaded contract artifacts")
	flag.StringVar(&outputPath, "output", "", "path to write markdown summary")
	flag.StringVar(&jsonOutputPath, "json-output", "", "path to write JSON summary")
	flag.StringVar(&servicesJSON, "services-json", "", "JSON object with selected services, e.g. {\"service\":[\"rest_api\"]}")
	flag.Parse()

	selectedServices, err := resolveServices(servicesJSON)
	if err != nil {
		fatalf("resolve services: %v", err)
	}

	rootDir, err := os.Getwd()
	if err != nil {
		fatalf("get working directory: %v", err)
	}

	summaries, err := buildSummaries(rootDir, artifactsDir, selectedServices)
	if err != nil {
		fatalf("build summaries: %v", err)
	}

	markdown := renderMarkdown(summaries)
	if outputPath != "" {
		if err := os.WriteFile(outputPath, []byte(markdown), 0o644); err != nil {
			fatalf("write markdown summary: %v", err)
		}
	} else {
		fmt.Print(markdown)
	}

	if jsonOutputPath != "" {
		payload, err := json.MarshalIndent(summaries, "", "  ")
		if err != nil {
			fatalf("marshal JSON summary: %v", err)
		}
		if err := os.WriteFile(jsonOutputPath, payload, 0o644); err != nil {
			fatalf("write JSON summary: %v", err)
		}
	}
}

func buildSummaries(rootDir, artifactsDir string, selectedServices []string) ([]serviceSummary, error) {
	summaries := make([]serviceSummary, 0, len(selectedServices))
	for _, serviceDir := range selectedServices {
		summary, err := summarizeService(rootDir, artifactsDir, serviceDir)
		if err != nil {
			return nil, fmt.Errorf("summarize %s: %w", serviceDir, err)
		}
		summaries = append(summaries, summary)
	}
	return summaries, nil
}

func summarizeService(rootDir, artifactsDir, serviceDir string) (serviceSummary, error) {
	catalogPath := filepath.Join(rootDir, serviceDir, "catalog.yaml")
	catalog, err := loadCatalog(catalogPath)
	if err != nil {
		return serviceSummary{}, err
	}

	endpoints := flattenCatalogEndpoints(catalog)
	rows, err := loadReportRows(filepath.Join(rootDir, serviceDir, "REPORT.md"))
	if err != nil {
		return serviceSummary{}, err
	}

	assignReportStatuses(endpoints, rows)

	testSummary, err := loadPackageSummary(artifactsDir, serviceDir)
	if err != nil {
		return serviceSummary{}, err
	}

	applyFallbackStatuses(serviceDir, endpoints, testSummary.PackageStatuses)

	counts := make(map[string]int, len(statusOrder))
	for _, status := range statusOrder {
		counts[status] = 0
	}
	for _, endpoint := range endpoints {
		counts[endpoint.Status]++
	}

	return serviceSummary{
		ServiceDir: serviceDir,
		Module:     catalog.APIService,
		Issue:      issueByService[serviceDir],
		Total:      len(endpoints),
		Counts:     counts,
		PassRate:   formatPassRate(counts[statusGreen], len(endpoints)),
		Endpoints:  endpoints,
	}, nil
}

func resolveServices(servicesJSON string) ([]string, error) {
	if strings.TrimSpace(servicesJSON) == "" {
		return append([]string(nil), serviceOrder...), nil
	}

	var selection matrixSelection
	if err := json.Unmarshal([]byte(servicesJSON), &selection); err != nil {
		return nil, fmt.Errorf("parse services JSON: %w", err)
	}
	if len(selection.Service) == 0 {
		return append([]string(nil), serviceOrder...), nil
	}

	allowed := make(map[string]struct{}, len(serviceOrder))
	for _, service := range serviceOrder {
		allowed[service] = struct{}{}
	}

	var ordered []string
	for _, service := range serviceOrder {
		for _, selected := range selection.Service {
			if selected == service {
				ordered = append(ordered, service)
				break
			}
		}
	}

	for _, selected := range selection.Service {
		if _, ok := allowed[selected]; !ok {
			return nil, fmt.Errorf("unknown service %q", selected)
		}
	}

	return ordered, nil
}

func loadCatalog(path string) (catalogFile, error) {
	data, err := os.ReadFile(path)
	if err != nil {
		return catalogFile{}, fmt.Errorf("read catalog: %w", err)
	}

	var catalog catalogFile
	if err := yaml.Unmarshal(data, &catalog); err != nil {
		return catalogFile{}, fmt.Errorf("parse catalog YAML: %w", err)
	}
	return catalog, nil
}

func flattenCatalogEndpoints(catalog catalogFile) []endpointStatus {
	var endpoints []endpointStatus
	for _, category := range catalog.Categories {
		for _, endpoint := range category.Endpoints {
			endpoints = append(endpoints, endpointStatus{
				Name:     endpoint.Name,
				Method:   strings.TrimSpace(endpoint.Method),
				Path:     strings.TrimSpace(endpoint.Path),
				Coverage: strings.TrimSpace(endpoint.Coverage),
				TestFile: strings.TrimSpace(endpoint.TestFile),
			})
		}
	}
	return endpoints
}

func loadReportRows(path string) ([]reportRow, error) {
	data, err := os.ReadFile(path)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, nil
		}
		return nil, fmt.Errorf("read report: %w", err)
	}

	lines := strings.Split(string(data), "\n")
	rows := make([]reportRow, 0)
	for _, line := range lines {
		row, ok := parseReportRow(line)
		if ok {
			rows = append(rows, row)
		}
	}

	sort.Slice(rows, func(i, j int) bool {
		return len(rows[i].PathCell) > len(rows[j].PathCell)
	})
	return rows, nil
}

func parseReportRow(line string) (reportRow, bool) {
	trimmed := strings.TrimSpace(line)
	if !strings.HasPrefix(trimmed, "|") || !strings.HasSuffix(trimmed, "|") {
		return reportRow{}, false
	}

	parts := strings.Split(trimmed, "|")
	if len(parts) < 4 {
		return reportRow{}, false
	}

	status := ""
	pathCell := ""
	for _, part := range parts[1 : len(parts)-1] {
		cell := strings.TrimSpace(part)
		if cell == "" || isMarkdownSeparator(cell) {
			continue
		}
		if status == "" {
			status = extractStatus(cell)
		}
		if pathCell == "" && strings.Contains(cell, "/") {
			pathCell = normalizeMarkdown(cell)
		}
	}

	if status == "" || pathCell == "" {
		return reportRow{}, false
	}

	return reportRow{PathCell: pathCell, Status: status}, true
}

func isMarkdownSeparator(cell string) bool {
	cell = strings.ReplaceAll(cell, ":", "")
	cell = strings.ReplaceAll(cell, "-", "")
	return strings.TrimSpace(cell) == ""
}

func extractStatus(cell string) string {
	for _, status := range statusOrder {
		if strings.Contains(cell, status) {
			return status
		}
	}
	return ""
}

func normalizeMarkdown(cell string) string {
	cell = strings.ReplaceAll(cell, "`", "")
	return strings.Join(strings.Fields(cell), " ")
}

func assignReportStatuses(endpoints []endpointStatus, rows []reportRow) {
	for i := range endpoints {
		for _, row := range rows {
			if rowMatchesEndpoint(row.PathCell, endpoints[i]) {
				endpoints[i].Status = row.Status
				break
			}
		}
	}
}

func rowMatchesEndpoint(pathCell string, endpoint endpointStatus) bool {
	if !methodMentioned(pathCell, endpoint.Method) {
		return false
	}
	return pathMentioned(pathCell, endpoint.Path)
}

func methodMentioned(cell, method string) bool {
	for idx := strings.Index(cell, method); idx >= 0; {
		beforeOK := idx == 0 || !unicode.IsLetter(rune(cell[idx-1]))
		afterIdx := idx + len(method)
		afterOK := afterIdx == len(cell) || !unicode.IsLetter(rune(cell[afterIdx]))
		if beforeOK && afterOK {
			return true
		}
		nextStart := idx + 1
		next := strings.Index(cell[nextStart:], method)
		if next < 0 {
			break
		}
		idx = nextStart + next
	}
	return false
}

func pathMentioned(cell, path string) bool {
	for idx := strings.Index(cell, path); idx >= 0; {
		beforeOK := idx == 0 || !isPathRune(rune(cell[idx-1]))
		afterIdx := idx + len(path)
		afterOK := afterIdx == len(cell) || !isPathRune(rune(cell[afterIdx]))
		if beforeOK && afterOK {
			return true
		}
		nextStart := idx + 1
		next := strings.Index(cell[nextStart:], path)
		if next < 0 {
			break
		}
		idx = nextStart + next
	}
	return false
}

func isPathRune(r rune) bool {
	return unicode.IsLetter(r) || unicode.IsDigit(r) || r == '/' || r == '{' || r == '}' || r == '_' || r == '-'
}

func loadPackageSummary(artifactsDir, serviceDir string) (packageRunSummary, error) {
	summary := packageRunSummary{
		PackageStatuses: make(map[string]string),
	}
	if strings.TrimSpace(artifactsDir) == "" {
		return summary, nil
	}

	filePath, err := findArtifactFile(artifactsDir, serviceDir+".log", serviceDir+".json")
	if err != nil {
		return summary, err
	}
	if filePath == "" {
		return summary, nil
	}

	switch filepath.Ext(filePath) {
	case ".json":
		return loadPackageSummaryFromJSON(filePath, serviceDir)
	default:
		return loadPackageSummaryFromLog(filePath, serviceDir)
	}
}

func findArtifactFile(root string, basenames ...string) (string, error) {
	wanted := make(map[string]struct{}, len(basenames))
	for _, name := range basenames {
		wanted[name] = struct{}{}
	}

	var matches []string
	err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
		if err != nil {
			return err
		}
		if d.IsDir() {
			return nil
		}
		if _, ok := wanted[d.Name()]; ok {
			matches = append(matches, path)
		}
		return nil
	})
	if err != nil {
		return "", fmt.Errorf("walk artifacts directory: %w", err)
	}
	if len(matches) == 0 {
		return "", nil
	}

	sort.Slice(matches, func(i, j int) bool {
		depthI := strings.Count(filepath.ToSlash(matches[i]), "/")
		depthJ := strings.Count(filepath.ToSlash(matches[j]), "/")
		if depthI != depthJ {
			return depthI < depthJ
		}
		return matches[i] < matches[j]
	})
	return matches[0], nil
}

func loadPackageSummaryFromLog(filePath, serviceDir string) (packageRunSummary, error) {
	summary := packageRunSummary{
		PackageStatuses: make(map[string]string),
	}

	file, err := os.Open(filePath)
	if err != nil {
		return summary, fmt.Errorf("open test log: %w", err)
	}
	defer file.Close()

	prefix := "github.com/nexuscrm/tests/contract/" + serviceDir
	scanner := bufio.NewScanner(file)
	scanner.Buffer(make([]byte, 0, 64*1024), 16*1024*1024)
	for scanner.Scan() {
		fields := strings.Fields(scanner.Text())
		if len(fields) < 2 {
			continue
		}
		packagePath := fields[1]
		if !strings.HasPrefix(packagePath, prefix) {
			continue
		}

		switch fields[0] {
		case "ok":
			summary.PackageStatuses[packagePath] = "pass"
		case "FAIL":
			summary.PackageStatuses[packagePath] = "fail"
		case "?":
			summary.PackageStatuses[packagePath] = "skip"
		}
	}
	if err := scanner.Err(); err != nil {
		return summary, fmt.Errorf("scan test log: %w", err)
	}
	return summary, nil
}

func loadPackageSummaryFromJSON(filePath, serviceDir string) (packageRunSummary, error) {
	summary := packageRunSummary{
		PackageStatuses: make(map[string]string),
	}

	file, err := os.Open(filePath)
	if err != nil {
		return summary, fmt.Errorf("open test JSON: %w", err)
	}
	defer file.Close()

	prefix := "github.com/nexuscrm/tests/contract/" + serviceDir
	scanner := bufio.NewScanner(file)
	scanner.Buffer(make([]byte, 0, 64*1024), 16*1024*1024)
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		if line == "" {
			continue
		}

		var event goTestEvent
		if err := json.Unmarshal([]byte(line), &event); err != nil {
			return summary, fmt.Errorf("parse test JSON line: %w", err)
		}
		if event.Test != "" || event.Package == "" || !strings.HasPrefix(event.Package, prefix) {
			continue
		}

		switch event.Action {
		case "pass", "fail", "skip":
			summary.PackageStatuses[event.Package] = event.Action
		}
	}
	if err := scanner.Err(); err != nil {
		return summary, fmt.Errorf("scan test JSON: %w", err)
	}
	return summary, nil
}

func applyFallbackStatuses(serviceDir string, endpoints []endpointStatus, packageStatuses map[string]string) {
	for i := range endpoints {
		if endpoints[i].Status != "" {
			continue
		}

		switch strings.ToLower(strings.TrimSpace(endpoints[i].Coverage)) {
		case "", "todo":
			endpoints[i].Status = statusNoCoverage
			continue
		}

		packagePath := packagePathForTestFile(serviceDir, endpoints[i].TestFile)
		switch packageStatuses[packagePath] {
		case "pass":
			endpoints[i].Status = statusGreen
		case "fail":
			endpoints[i].Status = statusRed
		default:
			endpoints[i].Status = statusNoCoverage
		}
	}
}

func packagePathForTestFile(serviceDir, testFile string) string {
	base := "github.com/nexuscrm/tests/contract/" + serviceDir
	dir := filepath.ToSlash(filepath.Dir(strings.TrimSpace(testFile)))
	if dir == "." || dir == "" {
		return base
	}
	return base + "/" + dir
}

func formatPassRate(green, total int) string {
	if total == 0 {
		return "n/a"
	}
	return fmt.Sprintf("%.1f%% (%d/%d)", float64(green)*100/float64(total), green, total)
}

func renderMarkdown(summaries []serviceSummary) string {
	var b strings.Builder
	b.WriteString("# Backend Contract Compatibility Summary\n\n")
	b.WriteString("Generated from checked-in service reports plus the latest `go test` artifacts.\n\n")
	b.WriteString("## Legend\n\n")
	b.WriteString(fmt.Sprintf("- `%s` Fully compatible with documented Salesforce behavior\n", statusGreen))
	b.WriteString(fmt.Sprintf("- `%s` Usable, but with tested behavioral or schema deviations\n", statusYellow))
	b.WriteString(fmt.Sprintf("- `%s` Implemented but incompatible, unusable, or seriously wrong\n", statusRed))
	b.WriteString(fmt.Sprintf("- `%s` No test coverage or current live evidence to verify yet\n", statusNoCoverage))
	b.WriteString(fmt.Sprintf("- `%s` Not implemented\n\n", statusNotImplemented))
	b.WriteString("## Module Summary\n\n")
	b.WriteString("| Module | Issue | Total | ")
	for _, status := range statusOrder {
		b.WriteString(status)
		b.WriteString(" | ")
	}
	b.WriteString("Pass Rate |\n")
	b.WriteString("| --- | --- | ---: | ---: | ---: | ---: | ---: | ---: | --- |\n")

	for _, summary := range summaries {
		fmt.Fprintf(&b, "| %s | [#%d](%s) | %d | %d | %d | %d | %d | %d | %s |\n",
			summary.Module,
			summary.Issue.Number,
			summary.Issue.URL,
			summary.Total,
			summary.Counts[statusGreen],
			summary.Counts[statusYellow],
			summary.Counts[statusRed],
			summary.Counts[statusNoCoverage],
			summary.Counts[statusNotImplemented],
			summary.PassRate,
		)
	}

	return b.String()
}

func fatalf(format string, args ...any) {
	fmt.Fprintf(os.Stderr, format+"\n", args...)
	os.Exit(1)
}
