package main

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"sort"
	"strings"

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

// ============================================================================
// SchemaDefinitions.ts Generation (from shared/constants/*.json)
// ============================================================================

// SystemConfig matches shared/constants/system.json
type SystemConfig struct {
	Profiles         map[string]ProfileDef    `json:"profiles"`
	SystemFields     map[string]SystemField   `json:"systemFields"`
	SystemTables     map[string]string        `json:"systemTables"`
	Permissions      map[string]PermissionDef `json:"permissions"`
	LogLevels        map[string]string        `json:"logLevels"`
	ObjectCategories map[string]string        `json:"objectCategories"`
	Defaults         map[string]interface{}   `json:"defaults"`
}

type ProfileDef struct {
	ID          string `json:"id"`
	Label       string `json:"label"`
	Description string `json:"description"`
	IsSystem    bool   `json:"is_system"`
	IsSuperUser bool   `json:"is_super_user"`
}

type SystemField struct {
	APIName   string `json:"apiName"`
	Label     string `json:"label"`
	ProtoName string `json:"protoName"`
}

type PermissionDef struct {
	Value     string `json:"value"`
	ProtoName string `json:"protoName"`
}

// FieldTypeDef matches shared/constants/fieldTypes.json entries
type FieldTypeDef struct {
	SQLType           *string  `json:"sqlType"`
	Icon              string   `json:"icon"`
	Label             string   `json:"label"`
	Description       string   `json:"description"`
	IsSearchable      bool     `json:"isSearchable"`
	IsGroupable       bool     `json:"isGroupable"`
	IsSummable        bool     `json:"isSummable"`
	ValidationPattern *string  `json:"validationPattern,omitempty"`
	ValidationMessage *string  `json:"validationMessage,omitempty"`
	IsFK              *bool    `json:"isFK,omitempty"`
	IsVirtual         *bool    `json:"isVirtual,omitempty"`
	IsSystemOnly      *bool    `json:"isSystemOnly,omitempty"`
	Operators         []string `json:"operators"`
}

// OperatorDef matches shared/constants/operators.json entries
type OperatorDef struct {
	Label         string `json:"label"`
	Symbol        string `json:"symbol"`
	SQLOperator   string `json:"sqlOperator"`
	SQLPattern    string `json:"sqlPattern,omitempty"`
	RequiresRange bool   `json:"requiresRange,omitempty"`
	RequiresList  bool   `json:"requiresList,omitempty"`
	NoValue       bool   `json:"noValue,omitempty"`
}

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

	// Read system.json
	systemPath := filepath.Join(projectRoot, "shared", "constants", "system.json")
	systemData, err := os.ReadFile(systemPath)
	if err != nil {
		return appErrors.NewInternalError("read system.json", err)
	}
	var sysConfig SystemConfig
	if err := json.Unmarshal(systemData, &sysConfig); err != nil {
		return appErrors.NewValidationError("system.json", "failed to parse JSON: "+err.Error())
	}

	// Read fieldTypes.json
	fieldTypesPath := filepath.Join(projectRoot, "shared", "constants", "fieldTypes.json")
	fieldTypesData, err := os.ReadFile(fieldTypesPath)
	if err != nil {
		return appErrors.NewInternalError("read fieldTypes.json", err)
	}
	var fieldTypes map[string]FieldTypeDef
	if err := json.Unmarshal(fieldTypesData, &fieldTypes); err != nil {
		return appErrors.NewValidationError("fieldTypes.json", "failed to parse JSON: "+err.Error())
	}

	// Read operators.json
	operatorsPath := filepath.Join(projectRoot, "shared", "constants", "operators.json")
	operatorsData, err := os.ReadFile(operatorsPath)
	if err != nil {
		return appErrors.NewInternalError("read operators.json", err)
	}
	var operators map[string]OperatorDef
	if err := json.Unmarshal(operatorsData, &operators); err != nil {
		return appErrors.NewValidationError("operators.json", "failed to parse JSON: "+err.Error())
	}

	// Header
	sb.WriteString("// Code generated by cmd/codegen. DO NOT EDIT.\n")
	sb.WriteString("// Source: shared/constants/*.json\n")
	sb.WriteString("// Generated at: " + ctx.timestamp + "\n\n")

	// ==================== System Tables ====================
	sb.WriteString("// ==================== System Tables ====================\n")
	sb.WriteString("// Re-exported from generated-schema.ts\n")
	sb.WriteString("// SYSTEM_TABLE_NAMES is the single source of truth (generated from backend)\n")
	sb.WriteString("// FieldType is also imported from generated-schema to ensure single definition.\n\n")

	sb.WriteString("import { SYSTEM_TABLE_NAMES, type SystemTableName, type FieldType } from '../../generated-schema';\n")
	sb.WriteString("export { SYSTEM_TABLE_NAMES, type SystemTableName, type FieldType };\n\n")

	sb.WriteString("export function isSystemTable(objectApiName: string): boolean {\n")
	sb.WriteString(fmt.Sprintf("    return objectApiName.startsWith('%s');\n", SystemTablePrefix))
	sb.WriteString("}\n\n")

	// ==================== Custom Objects ====================
	sb.WriteString("// ==================== Custom Objects ====================\n\n")
	sb.WriteString("export function isCustomObject(objectApiName: string): boolean {\n")
	sb.WriteString("    return objectApiName.endsWith('__c');\n")
	sb.WriteString("}\n\n")

	// ==================== Object Categories ====================
	sb.WriteString("// ==================== Object Categories ====================\n\n")
	sb.WriteString("export const OBJECT_CATEGORIES = {\n")
	var catKeys []string
	for k := range sysConfig.ObjectCategories {
		catKeys = append(catKeys, k)
	}
	sort.Strings(catKeys)
	for _, k := range catKeys {
		sb.WriteString(fmt.Sprintf("    \"%s\": \"%s\",\n", k, sysConfig.ObjectCategories[k]))
	}
	sb.WriteString("} as const;\n\n")
	sb.WriteString("export type ObjectCategory = typeof OBJECT_CATEGORIES[keyof typeof OBJECT_CATEGORIES];\n\n")

	// getObjectCategory function
	sb.WriteString("export function getObjectCategory(objectApiName: string): ObjectCategory {\n")
	sb.WriteString("    if (isSystemTable(objectApiName)) {\n")
	sb.WriteString("        const securityTables: SystemTableName[] = [\n")
	sb.WriteString("            SYSTEM_TABLE_NAMES.PROFILE,\n")
	sb.WriteString("            SYSTEM_TABLE_NAMES.USERROLE,\n")
	sb.WriteString("            SYSTEM_TABLE_NAMES.OBJECTPERMISSIONS,\n")
	sb.WriteString("            SYSTEM_TABLE_NAMES.FIELDPERMISSIONS,\n")
	sb.WriteString("            SYSTEM_TABLE_NAMES.SHARINGRULE,\n")
	sb.WriteString("        ];\n\n")
	sb.WriteString("        const utilityTables: SystemTableName[] = [\n")
	sb.WriteString("            SYSTEM_TABLE_NAMES.SYSTEM_RECYCLEBIN,\n")
	sb.WriteString("            SYSTEM_TABLE_NAMES.SYSTEM_LOG,\n")
	sb.WriteString("            SYSTEM_TABLE_NAMES.SYSTEM_RECENT,\n")
	sb.WriteString("            SYSTEM_TABLE_NAMES.SYSTEM_CONFIG,\n")
	sb.WriteString("        ];\n\n")
	sb.WriteString("        if (securityTables.includes(objectApiName as SystemTableName)) {\n")
	sb.WriteString("            return OBJECT_CATEGORIES.SECURITY;\n")
	sb.WriteString("        } else if (utilityTables.includes(objectApiName as SystemTableName)) {\n")
	sb.WriteString("            return OBJECT_CATEGORIES.UTILITY;\n")
	sb.WriteString("        }\n")
	sb.WriteString("        return OBJECT_CATEGORIES.SYSTEM_METADATA;\n")
	sb.WriteString("    }\n\n")
	sb.WriteString("    return OBJECT_CATEGORIES.BUSINESS_CUSTOM;\n")
	sb.WriteString("}\n\n")

	// ==================== Field Types ====================
	sb.WriteString("// ==================== Field Types ====================\n\n")

	// Generate FieldType union type - SKIPPED (Imported from generated-schema.ts)
	var ftKeys []string
	for k := range fieldTypes {
		ftKeys = append(ftKeys, k)
	}
	sort.Strings(ftKeys)

	// FieldTypeDefinition interface
	sb.WriteString("export interface FieldTypeDefinition {\n")
	sb.WriteString("    sqlType: string | null;\n")
	sb.WriteString("    icon: string;\n")
	sb.WriteString("    label: string;\n")
	sb.WriteString("    description: string;\n")
	sb.WriteString("    isSearchable: boolean;\n")
	sb.WriteString("    isGroupable: boolean;\n")
	sb.WriteString("    isSummable: boolean;\n")
	sb.WriteString("    validationPattern?: string;\n")
	sb.WriteString("    validationMessage?: string;\n")
	sb.WriteString("    isFK?: boolean;\n")
	sb.WriteString("    isVirtual?: boolean;\n")
	sb.WriteString("    isSystemOnly?: boolean;\n")
	sb.WriteString("    operators: string[];\n")
	sb.WriteString("}\n\n")

	// FIELD_TYPES
	sb.WriteString("export const FIELD_TYPES: Record<FieldType, FieldTypeDefinition> = {\n")
	for _, k := range ftKeys {
		ft := fieldTypes[k]
		sb.WriteString(fmt.Sprintf("    \"%s\": {\n", k))
		if ft.SQLType != nil {
			sb.WriteString(fmt.Sprintf("        \"sqlType\": \"%s\",\n", *ft.SQLType))
		} else {
			sb.WriteString("        \"sqlType\": null,\n")
		}
		sb.WriteString(fmt.Sprintf("        \"icon\": \"%s\",\n", ft.Icon))
		sb.WriteString(fmt.Sprintf("        \"label\": \"%s\",\n", ft.Label))
		sb.WriteString(fmt.Sprintf("        \"description\": \"%s\",\n", ft.Description))
		sb.WriteString(fmt.Sprintf("        \"isSearchable\": %t,\n", ft.IsSearchable))
		sb.WriteString(fmt.Sprintf("        \"isGroupable\": %t,\n", ft.IsGroupable))
		sb.WriteString(fmt.Sprintf("        \"isSummable\": %t,\n", ft.IsSummable))
		if ft.ValidationPattern != nil {
			// Escape backslashes for JSON
			escaped := strings.ReplaceAll(*ft.ValidationPattern, "\\", "\\\\")
			sb.WriteString(fmt.Sprintf("        \"validationPattern\": \"%s\",\n", escaped))
		}
		if ft.ValidationMessage != nil {
			sb.WriteString(fmt.Sprintf("        \"validationMessage\": \"%s\",\n", *ft.ValidationMessage))
		}
		if ft.IsFK != nil && *ft.IsFK {
			sb.WriteString("        \"isFK\": true,\n")
		}
		if ft.IsVirtual != nil && *ft.IsVirtual {
			sb.WriteString("        \"isVirtual\": true,\n")
		}
		if ft.IsSystemOnly != nil && *ft.IsSystemOnly {
			sb.WriteString("        \"isSystemOnly\": true,\n")
		}
		sb.WriteString("        \"operators\": [\n")
		for _, op := range ft.Operators {
			sb.WriteString(fmt.Sprintf("            \"%s\",\n", op))
		}
		sb.WriteString("        ]\n")
		sb.WriteString("    },\n")
	}
	sb.WriteString("};\n\n")

	// Helper functions for field types
	sb.WriteString("export function isSummableType(type: string): boolean {\n")
	sb.WriteString("    return FIELD_TYPES[type as FieldType]?.isSummable ?? false;\n")
	sb.WriteString("}\n\n")

	// ==================== Operators ====================
	sb.WriteString("// ==================== Operators ====================\n\n")

	sb.WriteString("export interface OperatorDefinition {\n")
	sb.WriteString("    label: string;\n")
	sb.WriteString("    symbol: string;\n")
	sb.WriteString("    sqlOperator: string;\n")
	sb.WriteString("    sqlPattern?: string;\n")
	sb.WriteString("    requiresRange?: boolean;\n")
	sb.WriteString("    requiresList?: boolean;\n")
	sb.WriteString("    noValue?: boolean;\n")
	sb.WriteString("}\n\n")

	sb.WriteString("export const OPERATORS: Record<string, OperatorDefinition> = {\n")
	var opKeys []string
	for k := range operators {
		opKeys = append(opKeys, k)
	}
	sort.Strings(opKeys)
	for _, k := range opKeys {
		op := operators[k]
		sb.WriteString(fmt.Sprintf("    \"%s\": {\n", k))
		sb.WriteString(fmt.Sprintf("        \"label\": \"%s\",\n", op.Label))
		sb.WriteString(fmt.Sprintf("        \"symbol\": \"%s\",\n", op.Symbol))
		sb.WriteString(fmt.Sprintf("        \"sqlOperator\": \"%s\",\n", op.SQLOperator))
		if op.SQLPattern != "" {
			sb.WriteString(fmt.Sprintf("        \"sqlPattern\": \"%s\",\n", op.SQLPattern))
		}
		if op.RequiresRange {
			sb.WriteString("        \"requiresRange\": true,\n")
		}
		if op.RequiresList {
			sb.WriteString("        \"requiresList\": true,\n")
		}
		if op.NoValue {
			sb.WriteString("        \"noValue\": true,\n")
		}
		sb.WriteString("    },\n")
	}
	sb.WriteString("};\n\n")

	sb.WriteString("export function getOperatorsForType(type: FieldType): string[] {\n")
	sb.WriteString("    return FIELD_TYPES[type]?.operators ?? [];\n")
	sb.WriteString("}\n")

	// Write file
	outPath := filepath.Join(projectRoot, "frontend", "src", "core", "constants", "SchemaDefinitions.ts")
	if err := os.WriteFile(outPath, []byte(sb.String()), 0644); err != nil {
		return appErrors.NewInternalError("write SchemaDefinitions.ts", err)
	}
	fmt.Printf("✅ Generated: %s (%d bytes)\n", outPath, sb.Len())
	return nil
}
