package main

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"

	appErrors "github.com/nexuscrm/backend/pkg/errors"
)

// ============================================================================
// Go Table Constants Generation
// ============================================================================

func generateGoTableConstants(ctx *genContext, projectRoot string) error {
	var sb strings.Builder

	sb.WriteString("// Code generated by cmd/codegen. DO NOT EDIT.\n")
	sb.WriteString("// Source: internal/bootstrap/system_tables.json\n")
	sb.WriteString("// Generated at: " + ctx.timestamp + "\n\n")
	sb.WriteString("package constants\n\n")

	sb.WriteString("// System Table Names\n")
	sb.WriteString("const (\n")

	// Sort tables for deterministic output
	for _, t := range ctx.tables {
		constName := tableNameToConstant(t.TableName)
		sb.WriteString(fmt.Sprintf("\t%s = \"%s\"\n", constName, t.TableName))
	}

	sb.WriteString(")\n\n")

	// Generate slice of all tables
	sb.WriteString("// AllSystemTableNames returns all system table names for validation\n")
	sb.WriteString("var AllSystemTableNames = []string{\n")
	for _, t := range ctx.tables {
		constName := tableNameToConstant(t.TableName)
		sb.WriteString(fmt.Sprintf("\t%s,\n", constName))
	}
	sb.WriteString("}\n")

	outPath := filepath.Join(projectRoot, "shared", "pkg", "constants", "z_generated_tables.go")
	if err := os.WriteFile(outPath, []byte(sb.String()), 0644); err != nil {
		return appErrors.NewInternalError("write z_generated_tables.go", err)
	}
	fmt.Printf("✅ Generated: %s (%d bytes)\n", outPath, sb.Len())
	return nil
}

// ============================================================================
// Go Field Constants Generation
// ============================================================================

func generateGoFieldConstants(ctx *genContext, projectRoot string) error {
	var sb strings.Builder

	sb.WriteString("// Code generated by cmd/codegen. DO NOT EDIT.\n")
	sb.WriteString("// Source: internal/bootstrap/system_tables.json\n")
	sb.WriteString("// Generated at: " + ctx.timestamp + "\n\n")
	sb.WriteString("package constants\n\n")

	// Generate common fields first (Salesforce Native)
	sb.WriteString("// Common field names (Salesforce Native)\n")
	sb.WriteString("const (\n")

	for _, field := range commonFieldNames {
		// ALGORITHMIC NAMING:	for _, field := range commonFieldNames {
		// ALGORITHMIC NAMING: Field + PascalCase
		// The input 'field' is now expected to be PascalCase (e.g. "Name") due to JSON update
		pascalName := field
		constSuffix := field

		// Special case handling for constant names (Backwards Compatibility for Codebase)
		if strings.ToLower(field) == "id" {
			pascalName = "Id"
			constSuffix = "ID" // FieldID
		} else if strings.ToLower(field) == "api_name" {
			pascalName = "ApiName"
			constSuffix = "APIName" // FieldAPIName (Capitalized acronym for constant)
		} else {
			pascalName = field                 // Value = "OwnerId"
			constSuffix = snakeToPascal(field) // Suffix = "OwnerID"
		}

		// FieldID = "Id"
		sb.WriteString(fmt.Sprintf("\tField%s = \"%s\"\n", constSuffix, pascalName))

		// ColID = "Id"
		sb.WriteString(fmt.Sprintf("\tCol%s = \"%s\"\n", constSuffix, pascalName))
	}
	sb.WriteString(")\n\n")

	// Generate table-specific field constants
	for _, t := range ctx.tables {
		tablePrefix := tableNameToPrefix(t.TableName)
		sb.WriteString(fmt.Sprintf("// %s fields\n", t.TableName))
		sb.WriteString("const (\n")

		for _, col := range dedupeColumns(t.Columns, func(col ColumnDef) string {
			pascal := snakeToPascal(col.Name)
			switch col.Name {
			case "Id":
				pascal = "ID"
			case "ApiName":
				pascal = "APIName"
			}
			return fmt.Sprintf("Field%s_%s", tablePrefix, pascal)
		}) {
			pascal := snakeToPascal(col.Name)
			constSuffix := pascal

			// Handle Acronyms in Constant Names
			if col.Name == "Id" {
				constSuffix = "ID"
			} else if col.Name == "ApiName" {
				constSuffix = "APIName"
			}
			// Add more acronyms if needed (URL, HTML, etc)

			// FieldSysTable_Name = "Name"
			constName := fmt.Sprintf("Field%s_%s", tablePrefix, constSuffix)
			sb.WriteString(fmt.Sprintf("\t%s = \"%s\"\n", constName, col.Name))

			// ColSysTable_Name = "Name"
			colConstName := fmt.Sprintf("Col%s_%s", tablePrefix, constSuffix)
			sb.WriteString(fmt.Sprintf("\t%s = \"%s\"\n", colConstName, col.Name))
		}
		sb.WriteString(")\n\n")
	}

	outPath := filepath.Join(projectRoot, "shared", "pkg", "constants", "z_generated_fields.go")
	if err := os.WriteFile(outPath, []byte(sb.String()), 0644); err != nil {
		return appErrors.NewInternalError("write z_generated_fields.go", err)
	}
	fmt.Printf("✅ Generated: %s (%d bytes)\n", outPath, sb.Len())
	return nil
}

// ============================================================================
// Go Column Lists Generation (Automated Prevention Mechanism)
// ============================================================================

func generateGoColumnLists(ctx *genContext, projectRoot string) error {
	var sb strings.Builder

	sb.WriteString("// Code generated by cmd/codegen. DO NOT EDIT.\n")
	sb.WriteString("// Source: internal/bootstrap/system_tables.json\n")
	sb.WriteString("// Generated at: " + ctx.timestamp + "\n\n")
	sb.WriteString("package constants\n\n")

	sb.WriteString("// ==================== SQL Column Lists ====================\n")
	sb.WriteString("// These lists are generated to prevent \"Ghost Column\" bugs and strict usage of Logical API Names.\n\n")

	sb.WriteString("var (\n")

	for _, t := range ctx.tables {
		// e.g. ColsSystemObject
		// Remove _System_ prefix for cleaner name: ColsObject
		// Or keep it simple: ColsSystemObject is also fine.
		// TableSystemObject -> ColsSystemObject.
		// TableName converts _System_Object to SystemObject struct name.
		// Let's use Cols + structName (e.g. ColsSystemObject)

		structName := tableNameToStructName(t.TableName)
		sliceName := fmt.Sprintf("Cols%s", structName)
		tablePrefix := tableNameToPrefix(t.TableName)

		sb.WriteString(fmt.Sprintf("\t%s = []string{\n", sliceName))

		for _, col := range dedupeColumns(t.Columns, func(col ColumnDef) string {
			return snakeToPascal(col.Name)
		}) {
			// Determine the constant name to use.
			var constName string
			pascalName := snakeToPascal(col.Name)

			// Common/System fields are now simplified
			// If col.Name is "Id", it maps to FieldID
			if col.Name == "Id" {
				constName = "FieldID"
			} else if col.Name == "CreatedDate" {
				constName = "FieldCreatedDate"
			} else if col.Name == "LastModifiedDate" {
				constName = "FieldLastModifiedDate"
			} else if col.Name == "CreatedById" {
				constName = "FieldCreatedByID"
			} else if col.Name == "LastModifiedById" {
				constName = "FieldLastModifiedByID"
			} else if col.Name == "OwnerId" {
				constName = "FieldOwnerID"
			} else if col.Name == "IsDeleted" {
				constName = "FieldIsDeleted"
			} else {
				// Table Specific
				// Format: Field<TablePrefix>_<PascalField>
				constName = fmt.Sprintf("Field%s_%s", tablePrefix, pascalName)
			}

			sb.WriteString(fmt.Sprintf("\t\t%s,\n", constName))
		}

		sb.WriteString("\t}\n\n")
	}

	sb.WriteString(")\n")

	outPath := filepath.Join(projectRoot, "shared", "pkg", "constants", "z_generated_columns.go")
	if err := os.WriteFile(outPath, []byte(sb.String()), 0644); err != nil {
		return appErrors.NewInternalError("write z_generated_columns.go", err)
	}
	fmt.Printf("✅ Generated: %s (%d bytes)\n", outPath, sb.Len())
	return nil
}
