/**
 * ContractForm — create a new energy contract OR add a version to an existing one.
 *
 * Form fields are dynamically rendered from the profile structure returned by
 * GET /api/energy/profiles — never hardcoded.  The profile structure provides
 * section names (energy, standing, credits), leaf keys, and their units.
 *
 * Create mode: show Name, Kind (Select from profiles), Currency, optional
 *   effective_from, then dynamically rendered profile value fields.
 * Add-version mode (contractId provided): show only effective_from (required)
 *   + profile value fields for the contract's kind.
 */

import { useState, useMemo } from 'react'
import {
  Modal,
  Stack,
  TextInput,
  NumberInput,
  Select,
  Button,
  Group,
  Alert,
  Loader,
  Center,
  Text,
  Divider,
  Title,
} from '@mantine/core'
import { useEnergyProfiles, useCreateContract, useAddContractVersion, useContractDetail } from './hooks'

// ---------------------------------------------------------------------------
// Props
// ---------------------------------------------------------------------------

export interface ContractFormProps {
  /** When provided: add-version mode; contract must exist. */
  contractId?: number
  /** Existing contract kind (for add-version mode or edit). */
  defaultKind?: string
  onClose: () => void
  onSaved: () => void
}

// ---------------------------------------------------------------------------
// Helpers for dynamic profile field rendering
// ---------------------------------------------------------------------------

/**
 * A leaf in the profile structure is any object with a `unit` key.
 * Returns true if the value is a leaf (renderable field).
 */
function isLeaf(val: unknown): val is { unit: string; default?: unknown } {
  return typeof val === 'object' && val !== null && 'unit' in val
}

/**
 * Extract all leaf fields from a profile section, returning
 * [dotPath, unit, defaultValue] tuples. E.g.:
 *   "energy.buy.normal" → "EUR/kWh"
 *   "energy.energy_tax" → "EUR/kWh"
 */
interface LeafField {
  /** Dot-separated path within the section, e.g. "buy.normal" */
  fieldPath: string
  unit: string
  defaultValue?: number
}

function extractLeafFields(obj: Record<string, unknown>, prefix = ''): LeafField[] {
  const fields: LeafField[] = []
  for (const [key, val] of Object.entries(obj)) {
    // Skip non-object values (e.g. dual_tariff: true)
    if (typeof val !== 'object' || val === null) continue
    const path = prefix ? `${prefix}.${key}` : key
    if (isLeaf(val)) {
      fields.push({
        fieldPath: path,
        unit: val.unit,
        defaultValue: typeof val.default === 'number' ? val.default : undefined,
      })
    } else {
      fields.push(...extractLeafFields(val as Record<string, unknown>, path))
    }
  }
  return fields
}

/**
 * Build a nested values object from flat field values.
 * E.g. { "buy.normal": 0.133, "buy.dal": 0.127 } → { buy: { normal: 0.133, dal: 0.127 } }
 */
function buildNestedValues(
  sectionFields: Record<string, LeafField[]>,
  fieldValues: Record<string, number | string>,
): Record<string, unknown> {
  const result: Record<string, unknown> = {}

  for (const [section, fields] of Object.entries(sectionFields)) {
    const sectionObj: Record<string, unknown> = {}
    for (const field of fields) {
      const raw = fieldValues[`${section}.${field.fieldPath}`]
      const numVal = typeof raw === 'number' ? raw : parseFloat(String(raw))
      // Set nested path
      const parts = field.fieldPath.split('.')
      let current = sectionObj
      for (let i = 0; i < parts.length - 1; i++) {
        if (!(parts[i] in current)) current[parts[i]] = {}
        current = current[parts[i]] as Record<string, unknown>
      }
      current[parts[parts.length - 1]] = isNaN(numVal) ? 0 : numVal
    }
    result[section] = sectionObj
  }

  return result
}

/**
 * Label formatter: converts "buy.normal" → "Buy Normal", "energy_tax" → "Energy Tax"
 */
function formatLabel(path: string): string {
  return path
    .replace(/\./g, ' ')
    .replace(/_/g, ' ')
    .replace(/\b\w/g, (c) => c.toUpperCase())
}

// ---------------------------------------------------------------------------
// Component
// ---------------------------------------------------------------------------

export function ContractForm({ contractId, defaultKind, onClose, onSaved }: ContractFormProps) {
  const isAddVersion = contractId != null

  // Profiles query
  const profilesQuery = useEnergyProfiles()

  // Contract detail query — only used in add-version mode to prefill from the open version.
  const contractDetailQuery = useContractDetail(contractId ?? 0)

  // Create mode fields
  const [name, setName] = useState('')
  const [selectedKind, setSelectedKind] = useState<string | null>(defaultKind ?? null)
  const [currency, setCurrency] = useState('EUR')
  // ISO date string "YYYY-MM-DD" for effective_from; empty = default to now
  const [effectiveFromStr, setEffectiveFromStr] = useState('')

  // Dynamic profile field values: key = "<section>.<fieldPath>", value = number.
  // User edits are stored here; for add-version mode this is merged with prefillValues.
  const [userEdits, setUserEdits] = useState<Record<string, number | string>>({})

  const [error, setError] = useState<string | null>(null)

  const createMutation = useCreateContract()
  const addVersionMutation = useAddContractVersion()

  const isBusy = createMutation.isPending || addVersionMutation.isPending

  // Resolve the effective kind (add-version mode uses defaultKind)
  const effectiveKind = isAddVersion ? (defaultKind ?? null) : selectedKind

  // Build profile options from API response
  const profiles = profilesQuery.data?.profiles ?? []
  const profileOptions = profiles.map((p: Record<string, unknown>) => ({
    value: p.kind as string,
    label: (p.label as string | undefined) ?? (p.kind as string),
  }))

  // Find the selected profile structure
  const selectedProfile = profiles.find((p: Record<string, unknown>) => p.kind === effectiveKind)

  // Extract section → leaf fields mapping from the selected profile (memoized for stable reference).
  const sectionFields = useMemo<Record<string, LeafField[]>>(() => {
    if (!selectedProfile) return {}
    const profileObj = selectedProfile as Record<string, unknown>
    const result: Record<string, LeafField[]> = {}
    for (const [key, val] of Object.entries(profileObj)) {
      if (key === 'kind' || key === 'label') continue
      if (typeof val === 'object' && val !== null && !isLeaf(val)) {
        result[key] = extractLeafFields(val as Record<string, unknown>)
      }
    }
    return result
  }, [selectedProfile])

  // Add-version prefill: compute the seeded values from the contract's open version.
  // Returns the flat fieldValues dict or null when data is not ready.
  const prefillValues = useMemo<Record<string, number | string> | null>(() => {
    if (!isAddVersion) return null
    if (contractDetailQuery.isLoading || contractDetailQuery.isError) return null
    if (Object.keys(sectionFields).length === 0) return null

    const detail = contractDetailQuery.data
    if (!detail || !detail.versions || detail.versions.length === 0) return null

    const versions = detail.versions as Array<{
      effective_from: string
      effective_to: string | null
      values: Record<string, unknown>
    }>
    let openVersion = versions.find((v) => v.effective_to == null)
    if (!openVersion) {
      openVersion = [...versions].sort((a, b) =>
        a.effective_from < b.effective_from ? 1 : -1
      )[0]
    }
    if (!openVersion?.values) return null

    const seeded: Record<string, number | string> = {}
    for (const [section, fields] of Object.entries(sectionFields)) {
      const sectionVal = (openVersion.values as Record<string, unknown>)[section]
      if (typeof sectionVal !== 'object' || sectionVal === null) continue
      for (const leaf of fields) {
        const parts = leaf.fieldPath.split('.')
        let cursor: unknown = sectionVal
        for (const part of parts) {
          if (typeof cursor !== 'object' || cursor === null) { cursor = undefined; break }
          cursor = (cursor as Record<string, unknown>)[part]
        }
        if (cursor != null && (typeof cursor === 'number' || typeof cursor === 'string')) {
          const numVal = typeof cursor === 'number' ? cursor : parseFloat(String(cursor))
          seeded[`${section}.${leaf.fieldPath}`] = isNaN(numVal) ? 0 : numVal
        }
      }
    }
    return Object.keys(seeded).length > 0 ? seeded : null
  }, [
    isAddVersion,
    contractDetailQuery.isLoading,
    contractDetailQuery.isError,
    contractDetailQuery.data,
    sectionFields,
  ])

  // The effective field values: user edits override prefill; prefill is the base.
  // In add-version mode: when the user has not yet edited any field (userEdits is empty),
  // prefillValues is used as the initial base.  Once the user edits any field, their
  // full merged value (prefill + override) is stored in userEdits.
  const fieldValues: Record<string, number | string> =
    isAddVersion && prefillValues !== null && Object.keys(userEdits).length === 0
      ? prefillValues
      : userEdits

  function handleFieldChange(key: string, val: number | string) {
    // On first edit in add-version mode, copy prefill into userEdits so we don't
    // lose the other prefilled values when the user changes one field.
    setUserEdits((prev) => {
      const base = isAddVersion && prefillValues !== null && Object.keys(prev).length === 0
        ? { ...prefillValues }
        : prev
      return { ...base, [key]: val }
    })
  }

  // Initialize default values when profile changes (create mode only)
  function initDefaults(kind: string | null) {
    if (!kind) return
    const profile = profiles.find((p: Record<string, unknown>) => p.kind === kind)
    if (!profile) return
    const profileObj = profile as Record<string, unknown>
    const defaults: Record<string, number | string> = {}
    for (const [sectionKey, sectionVal] of Object.entries(profileObj)) {
      if (sectionKey === 'kind' || sectionKey === 'label') continue
      if (typeof sectionVal === 'object' && sectionVal !== null && !isLeaf(sectionVal)) {
        const leaves = extractLeafFields(sectionVal as Record<string, unknown>)
        for (const leaf of leaves) {
          defaults[`${sectionKey}.${leaf.fieldPath}`] = leaf.defaultValue ?? 0
        }
      }
    }
    setUserEdits(defaults)
  }

  async function handleSubmit(e: React.FormEvent) {
    e.preventDefault()
    setError(null)

    if (!isAddVersion && !name.trim()) {
      setError('Contract name is required.')
      return
    }
    if (!effectiveKind) {
      setError('Contract kind is required.')
      return
    }

    const values = buildNestedValues(sectionFields, fieldValues)

    try {
      // Convert local date string to a naive local-midnight datetime string (no Z).
      // The backend interprets naive datetimes as server local wall-clock time,
      // so "2026-06-25T00:00:00" → CEST local midnight → stored as UTC 22:00Z.
      // When effectiveFromStr is empty, fall back to the current instant (aware,
      // sent as ISO with Z) so the backend stores it unchanged.
      const effectiveFromISO = effectiveFromStr
        ? `${effectiveFromStr}T00:00:00`
        : undefined

      if (isAddVersion) {
        const body = {
          effective_from: effectiveFromISO ?? new Date().toISOString(),
          values,
        }
        await addVersionMutation.mutateAsync({ id: contractId, body })
      } else {
        const body = {
          name: name.trim(),
          kind: effectiveKind,
          currency,
          values,
          ...(effectiveFromISO ? { effective_from: effectiveFromISO } : {}),
        }
        await createMutation.mutateAsync(body)
      }
      onSaved()
      onClose()
    } catch {
      setError('Failed to save. Please try again.')
    }
  }

  // ---------------------------------------------------------------------------
  // Render
  // ---------------------------------------------------------------------------

  const title = isAddVersion ? 'Add Contract Version' : 'New Energy Contract'

  return (
    <Modal
      opened
      onClose={onClose}
      title={title}
      size="md"
      data-testid="contract-form-modal"
    >
      {(profilesQuery.isLoading || (isAddVersion && contractDetailQuery.isLoading)) && (
        <Center py="md">
          <Loader size="sm" data-testid="contract-form-loading" />
        </Center>
      )}

      {profilesQuery.isError && (
        <Alert color="red" mb="sm" data-testid="contract-form-profiles-error">
          Failed to load pricing profiles. Cannot continue.
        </Alert>
      )}

      {isAddVersion && contractDetailQuery.isError && !contractDetailQuery.isLoading && (
        <Alert color="yellow" mb="sm" data-testid="contract-form-detail-error">
          Could not load existing contract values for prefill — fields start at 0.
        </Alert>
      )}

      {!profilesQuery.isLoading && !(isAddVersion && contractDetailQuery.isLoading) && (
        <form onSubmit={handleSubmit} data-testid="contract-form">
          <Stack gap="sm">
            {/* Create mode fields */}
            {!isAddVersion && (
              <>
                <TextInput
                  label="Contract name"
                  required
                  value={name}
                  onChange={(e) => setName(e.currentTarget.value)}
                  data-testid="contract-name"
                />

                <Select
                  label="Pricing kind"
                  required
                  data={profileOptions}
                  value={selectedKind}
                  onChange={(val) => {
                    setSelectedKind(val)
                    initDefaults(val)
                  }}
                  data-testid="contract-kind"
                />

                <TextInput
                  label="Currency"
                  value={currency}
                  onChange={(e) => setCurrency(e.currentTarget.value)}
                  data-testid="contract-currency"
                />
              </>
            )}

            {/* Effective from — required for add-version, optional for create */}
            <TextInput
              label={isAddVersion ? 'Effective from (required)' : 'Effective from (optional, defaults to now)'}
              description="Date in YYYY-MM-DD format"
              type="date"
              required={isAddVersion}
              value={effectiveFromStr}
              onChange={(e) => setEffectiveFromStr(e.currentTarget.value)}
              data-testid="contract-effective-from"
            />

            {/* Dynamic profile fields */}
            {effectiveKind && Object.keys(sectionFields).length > 0 && (
              <>
                <Divider mt="xs" />
                {Object.entries(sectionFields).map(([section, fields]) => (
                  <Stack key={section} gap="xs" data-testid={`contract-section-${section}`}>
                    <Title order={6} tt="capitalize" c="dimmed">
                      {section.replace(/_/g, ' ')}
                    </Title>
                    {fields.map((field) => {
                      const key = `${section}.${field.fieldPath}`
                      const raw = fieldValues[key]
                      const numVal = typeof raw === 'number' ? raw : parseFloat(String(raw))
                      return (
                        <NumberInput
                          key={key}
                          label={formatLabel(field.fieldPath)}
                          description={field.unit}
                          value={isNaN(numVal) ? 0 : numVal}
                          onChange={(val) => handleFieldChange(key, val)}
                          decimalScale={6}
                          step={0.001}
                          data-testid={`contract-field-${key}`}
                        />
                      )
                    })}
                  </Stack>
                ))}
              </>
            )}

            {!effectiveKind && !isAddVersion && (
              <Text size="sm" c="dimmed" data-testid="contract-form-kind-hint">
                Select a pricing kind to see the rate fields.
              </Text>
            )}

            {error && (
              <Alert color="red" data-testid="contract-form-error">
                {error}
              </Alert>
            )}

            <Group justify="flex-end" gap="sm">
              <Button
                type="button"
                variant="default"
                onClick={onClose}
                data-testid="contract-form-cancel"
              >
                Cancel
              </Button>
              <Button
                type="submit"
                loading={isBusy}
                data-testid="contract-form-submit"
              >
                {isAddVersion ? 'Add Version' : 'Create Contract'}
              </Button>
            </Group>
          </Stack>
        </form>
      )}
    </Modal>
  )
}
