import type { FunctionalComponent } from 'preact';

import type {
  AsukaAssemblyAdjacencyRule,
  AsukaAssemblyAnchorDefinition,
  AsukaAssemblyBomItemDefinition,
  AsukaAssemblyClearanceDefinition,
  AsukaAssemblyComponentDefinition,
} from '../../../types/assembly3d';
import { Button, SectionCard } from '../../core';
import {
  CheckboxField,
  CsvField,
  FormGrid,
  NumberField,
  SelectField,
  TextField,
} from '../../forms';

type ElementAssemblyFieldsProps = {
  t: (key: string, fallback?: string) => string;
  form: any;
};

const makeId = (prefix: string): string =>
  `${prefix}-${Math.random().toString(36).slice(2, 8)}`;

const readAssembly = (form: any): AsukaAssemblyComponentDefinition => {
  const value = form?.values?.assembly;
  return value?.schemaVersion === 1
    ? value
    : { schemaVersion: 1, anchors: [], clearances: [], adjacency: [], bom: [] };
};

const patchAssembly = (
  form: any,
  patch: Partial<AsukaAssemblyComponentDefinition>,
): void => {
  form.setValue('assembly', {
    ...readAssembly(form),
    ...patch,
    schemaVersion: 1,
  });
};

const updateItem = <T,>(
  items: T[] | undefined,
  index: number,
  patch: Partial<T>,
): T[] => (items ?? []).map((item, itemIndex) =>
  itemIndex === index ? { ...item, ...patch } : item);

const removeItem = <T,>(items: T[] | undefined, index: number): T[] =>
  (items ?? []).filter((_item, itemIndex) => itemIndex !== index);

const ItemHeader: FunctionalComponent<{
  title: string;
  removeLabel: string;
  onRemove: () => void;
}> = ({ title, removeLabel, onRemove }) => (
  <div className="ask-flex ask-items-center ask-justify-between ask-gap-3">
    <div className="ask-text-[12px] ask-font-semibold ask-text-text">{title}</div>
    <Button type="button" size="sm" variant="ghost" onClick={onRemove}>{removeLabel}</Button>
  </div>
);

export const ElementAssemblyFields: FunctionalComponent<ElementAssemblyFieldsProps> = ({ t, form }) => {
  const assembly = readAssembly(form);
  const enabled = form?.values?.assembly?.schemaVersion === 1;
  const anchors = assembly.anchors ?? [];
  const clearances = assembly.clearances ?? [];
  const adjacency = assembly.adjacency ?? [];
  const bom = assembly.bom ?? [];

  return (
    <SectionCard
      title={t('elements.assembly.title', 'Spatial assembly')}
      description={t(
        'elements.assembly.helper',
        'Make this model a reusable component that can snap, connect, validate spacing and generate production data.',
      )}
    >
      <div className="ask-flex ask-flex-col ask-gap-4">
        <CheckboxField
          label={t('elements.assembly.enabled', 'Use this element in spatial assemblies')}
          checked={enabled}
          onCheckedChange={(checked) => {
            if (checked) patchAssembly(form, {});
            else form.setValue('assembly', undefined);
          }}
        />

        {enabled ? (
          <>
            <FormGrid columns={3} className="max-lg:ask-grid-cols-1">
              <TextField
                label={t('elements.assembly.category', 'Component category')}
                hint={t('elements.assembly.categoryHint', 'Generic family used by adjacency and pricing rules.')}
                placeholder={t('elements.assembly.categoryPlaceholder', 'panel, frame, accessory...')}
                value={assembly.category ?? ''}
                onValueChange={(category) => patchAssembly(form, { category })}
              />
              <CsvField
                label={t('elements.assembly.tags', 'Tags')}
                placeholder={t('elements.assembly.tagsPlaceholder', 'indoor, structural, left')}
                value={assembly.tags ?? []}
                onValueChange={(tags) => patchAssembly(form, { tags })}
              />
              <SelectField
                label={t('elements.assembly.support', 'Base support')}
                hint={t('elements.assembly.supportHint', 'Floor keeps the component resting on the plan. Free keeps all axes editable.')}
                value={assembly.placement?.support ?? 'free'}
                options={[
                  { value: 'free', label: t('elements.assembly.supportFree', 'Free placement') },
                  { value: 'floor', label: t('elements.assembly.supportFloor', 'Plan floor') },
                ]}
                onValueChange={(support) => patchAssembly(form, {
                  placement: {
                    ...(assembly.placement ?? {}),
                    support: support === 'floor' ? 'floor' : 'free',
                  },
                })}
              />
            </FormGrid>

            <div className="ask-rounded-xl ask-bg-surfaceAlt ask-p-3">
              <div className="ask-mb-3 ask-text-[12px] ask-font-semibold ask-text-text">
                {t('elements.assembly.collision', 'Collision volume')}
              </div>
              <FormGrid columns={3} className="max-lg:ask-grid-cols-1">
                <CheckboxField
                  label={t('elements.assembly.collisionEnabled', 'Detect collisions')}
                  checked={assembly.collision?.enabled !== false}
                  onCheckedChange={(value) => patchAssembly(form, {
                    collision: { ...(assembly.collision ?? {}), enabled: value },
                  })}
                />
                <TextField
                  label={t('elements.assembly.collisionGroup', 'Collision group')}
                  value={assembly.collision?.group ?? 'solid'}
                  onValueChange={(group) => patchAssembly(form, {
                    collision: { ...(assembly.collision ?? {}), group },
                  })}
                />
                <NumberField
                  label={t('elements.assembly.collisionMargin', 'Safety margin')}
                  min={0}
                  step={0.001}
                  value={assembly.collision?.margin ?? 0.005}
                  onValueChange={(margin) => patchAssembly(form, {
                    collision: { ...(assembly.collision ?? {}), margin: margin ?? 0 },
                  })}
                />
              </FormGrid>
            </div>

            <div className="ask-flex ask-flex-col ask-gap-3">
              <div className="ask-flex ask-items-start ask-justify-between ask-gap-3">
                <div>
                  <div className="ask-text-[13px] ask-font-semibold ask-text-text">
                    {t('elements.assembly.anchors', 'Connection points')}
                  </div>
                  <div className="ask-text-[10px] ask-text-textMuted">
                    {t('elements.assembly.anchorsHint', 'Local points used to snap compatible components together.')}
                  </div>
                </div>
                <Button
                  type="button"
                  size="sm"
                  variant="secondary"
                  onClick={() => patchAssembly(form, {
                    anchors: [...anchors, {
                      id: makeId('anchor'),
                      label: `${t('elements.assembly.point', 'Point')} ${anchors.length + 1}`,
                      role: 'both',
                      position: { x: 0, y: 0, z: 0 },
                      normal: { x: 0, y: 0, z: 1 },
                      provides: ['generic'],
                      accepts: ['generic'],
                    }],
                  })}
                >
                  {t('elements.assembly.addPoint', 'Add point')}
                </Button>
              </div>
              {anchors.map((anchor: AsukaAssemblyAnchorDefinition, index: number) => (
                <div key={anchor.id || index} className="ask-flex ask-flex-col ask-gap-3 ask-border-t ask-border-borderSubtle ask-pt-3">
                  <ItemHeader
                    title={anchor.label || anchor.id}
                    removeLabel={t('common.remove', 'Remove')}
                    onRemove={() => patchAssembly(form, { anchors: removeItem(anchors, index) })}
                  />
                  <FormGrid columns={3} className="max-lg:ask-grid-cols-1">
                    <TextField
                      label={t('common.id', 'ID')}
                      value={anchor.id}
                      onValueChange={(id) => patchAssembly(form, { anchors: updateItem(anchors, index, { id }) })}
                    />
                    <TextField
                      label={t('common.label', 'Label')}
                      value={anchor.label ?? ''}
                      onValueChange={(label) => patchAssembly(form, { anchors: updateItem(anchors, index, { label }) })}
                    />
                    <SelectField
                      label={t('elements.assembly.role', 'Role')}
                      value={anchor.role ?? 'both'}
                      options={[
                        { value: 'both', label: t('elements.assembly.roleBoth', 'Bidirectional') },
                        { value: 'source', label: t('elements.assembly.roleSource', 'Source') },
                        { value: 'target', label: t('elements.assembly.roleTarget', 'Target') },
                      ]}
                      onValueChange={(role) => patchAssembly(form, {
                        anchors: updateItem(anchors, index, { role: role as AsukaAssemblyAnchorDefinition['role'] }),
                      })}
                    />
                  </FormGrid>
                  <FormGrid columns={3}>
                    {(['x', 'y', 'z'] as const).map((axis) => (
                      <NumberField
                        key={axis}
                        label={`${t('elements.assembly.position', 'Position')} ${axis.toUpperCase()}`}
                        step={0.01}
                        value={anchor.position?.[axis] ?? 0}
                        onValueChange={(value) => patchAssembly(form, {
                          anchors: updateItem(anchors, index, {
                            position: { ...anchor.position, [axis]: value ?? 0 },
                          }),
                        })}
                      />
                    ))}
                  </FormGrid>
                  <FormGrid columns={3} className="max-lg:ask-grid-cols-1">
                    <CsvField
                      label={t('elements.assembly.provides', 'Provides connectors')}
                      value={anchor.provides ?? []}
                      onValueChange={(provides) => patchAssembly(form, {
                        anchors: updateItem(anchors, index, { provides }),
                      })}
                    />
                    <CsvField
                      label={t('elements.assembly.accepts', 'Accepts connectors')}
                      value={anchor.accepts ?? []}
                      onValueChange={(accepts) => patchAssembly(form, {
                        anchors: updateItem(anchors, index, { accepts }),
                      })}
                    />
                    <NumberField
                      label={t('elements.assembly.snapDistance', 'Snap distance')}
                      min={0.001}
                      step={0.01}
                      value={anchor.snapDistance ?? 0.28}
                      onValueChange={(snapDistance) => patchAssembly(form, {
                        anchors: updateItem(anchors, index, { snapDistance: snapDistance ?? 0.28 }),
                      })}
                    />
                  </FormGrid>
                </div>
              ))}
            </div>

            <div className="ask-flex ask-flex-col ask-gap-3 ask-border-t ask-border-borderSubtle ask-pt-4">
              <div className="ask-flex ask-items-start ask-justify-between ask-gap-3">
                <div>
                  <div className="ask-text-[13px] ask-font-semibold ask-text-text">
                    {t('elements.assembly.clearances', 'Clearance zones')}
                  </div>
                  <div className="ask-text-[10px] ask-text-textMuted">
                    {t('elements.assembly.clearancesHint', 'Reserve access, movement or safety space around the component.')}
                  </div>
                </div>
                <Button
                  type="button"
                  size="sm"
                  variant="secondary"
                  onClick={() => patchAssembly(form, {
                    clearances: [...clearances, {
                      id: makeId('clearance'),
                      label: `${t('elements.assembly.clearance', 'Clearance')} ${clearances.length + 1}`,
                      center: { x: 0, y: 0, z: 0 },
                      size: { x: 1, y: 1, z: 1 },
                      severity: 'error',
                    }],
                  })}
                >
                  {t('elements.assembly.addClearance', 'Add zone')}
                </Button>
              </div>
              {clearances.map((clearance: AsukaAssemblyClearanceDefinition, index: number) => (
                <div key={clearance.id || index} className="ask-flex ask-flex-col ask-gap-3 ask-rounded-xl ask-bg-surfaceAlt ask-p-3">
                  <ItemHeader
                    title={clearance.label || clearance.id}
                    removeLabel={t('common.remove', 'Remove')}
                    onRemove={() => patchAssembly(form, { clearances: removeItem(clearances, index) })}
                  />
                  <FormGrid columns={3} className="max-lg:ask-grid-cols-1">
                    <TextField
                      label={t('common.label', 'Label')}
                      value={clearance.label ?? ''}
                      onValueChange={(label) => patchAssembly(form, { clearances: updateItem(clearances, index, { label }) })}
                    />
                    <SelectField
                      label={t('elements.assembly.severity', 'Severity')}
                      value={clearance.severity ?? 'error'}
                      options={[
                        { value: 'error', label: t('elements.assembly.blocking', 'Blocking') },
                        { value: 'warning', label: t('elements.assembly.warning', 'Warning') },
                      ]}
                      onValueChange={(severity) => patchAssembly(form, {
                        clearances: updateItem(clearances, index, { severity: severity as 'error' | 'warning' }),
                      })}
                    />
                    <TextField
                      label={t('elements.assembly.group', 'Target group')}
                      value={clearance.group ?? ''}
                      onValueChange={(group) => patchAssembly(form, { clearances: updateItem(clearances, index, { group }) })}
                    />
                  </FormGrid>
                  <FormGrid columns={3}>
                    {(['x', 'y', 'z'] as const).map((axis) => (
                      <NumberField
                        key={axis}
                        label={`${t('elements.assembly.size', 'Size')} ${axis.toUpperCase()}`}
                        min={0}
                        step={0.01}
                        value={clearance.size?.[axis] ?? 1}
                        onValueChange={(value) => patchAssembly(form, {
                          clearances: updateItem(clearances, index, {
                            size: { ...clearance.size, [axis]: value ?? 0 },
                          }),
                        })}
                      />
                    ))}
                  </FormGrid>
                </div>
              ))}
            </div>

            <div className="ask-flex ask-flex-col ask-gap-3 ask-border-t ask-border-borderSubtle ask-pt-4">
              <div className="ask-flex ask-items-start ask-justify-between ask-gap-3">
                <div>
                  <div className="ask-text-[13px] ask-font-semibold ask-text-text">
                    {t('elements.assembly.adjacency', 'Adjacency rules')}
                  </div>
                  <div className="ask-text-[10px] ask-text-textMuted">
                    {t('elements.assembly.adjacencyHint', 'Require or forbid nearby categories without domain-specific code.')}
                  </div>
                </div>
                <Button
                  type="button"
                  size="sm"
                  variant="secondary"
                  onClick={() => patchAssembly(form, {
                    adjacency: [...adjacency, {
                      id: makeId('rule'),
                      type: 'requires-neighbor',
                      target: { categories: [] },
                      maxDistance: 0.3,
                      severity: 'error',
                    }],
                  })}
                >
                  {t('elements.assembly.addRule', 'Add rule')}
                </Button>
              </div>
              {adjacency.map((rule: AsukaAssemblyAdjacencyRule, index: number) => (
                <div key={rule.id || index} className="ask-flex ask-flex-col ask-gap-3 ask-border-t ask-border-borderSubtle ask-pt-3">
                  <ItemHeader
                    title={rule.message || rule.id}
                    removeLabel={t('common.remove', 'Remove')}
                    onRemove={() => patchAssembly(form, { adjacency: removeItem(adjacency, index) })}
                  />
                  <FormGrid columns={3} className="max-lg:ask-grid-cols-1">
                    <SelectField
                      label={t('elements.assembly.ruleType', 'Rule')}
                      value={rule.type}
                      options={[
                        { value: 'requires-connection', label: t('elements.assembly.requiresConnection', 'Requires connection') },
                        { value: 'requires-neighbor', label: t('elements.assembly.requiresNeighbor', 'Requires neighbor') },
                        { value: 'forbids-neighbor', label: t('elements.assembly.forbidsNeighbor', 'Forbids neighbor') },
                        { value: 'min-gap', label: t('elements.assembly.minimumGap', 'Minimum gap') },
                        { value: 'max-gap', label: t('elements.assembly.maximumGap', 'Maximum gap') },
                      ]}
                      onValueChange={(type) => patchAssembly(form, {
                        adjacency: updateItem(adjacency, index, { type: type as AsukaAssemblyAdjacencyRule['type'] }),
                      })}
                    />
                    <CsvField
                      label={t('elements.assembly.targetCategories', 'Target categories')}
                      value={rule.target?.categories ?? []}
                      onValueChange={(categories) => patchAssembly(form, {
                        adjacency: updateItem(adjacency, index, {
                          target: { ...(rule.target ?? {}), categories },
                        }),
                      })}
                    />
                    <NumberField
                      label={t('elements.assembly.distance', 'Distance')}
                      min={0}
                      step={0.01}
                      value={rule.type === 'min-gap' ? rule.minDistance ?? 0.1 : rule.maxDistance ?? 0.3}
                      onValueChange={(distance) => patchAssembly(form, {
                        adjacency: updateItem(adjacency, index, rule.type === 'min-gap'
                          ? { minDistance: distance ?? 0 }
                          : { maxDistance: distance ?? 0 }),
                      })}
                    />
                  </FormGrid>
                </div>
              ))}
            </div>

            <div className="ask-flex ask-flex-col ask-gap-3 ask-border-t ask-border-borderSubtle ask-pt-4">
              <div className="ask-flex ask-items-start ask-justify-between ask-gap-3">
                <div>
                  <div className="ask-text-[13px] ask-font-semibold ask-text-text">
                    {t('elements.assembly.bom', 'Production items')}
                  </div>
                  <div className="ask-text-[10px] ask-text-textMuted">
                    {t('elements.assembly.bomHint', 'Lines generated in the bill of materials, quote and production export.')}
                  </div>
                </div>
                <Button
                  type="button"
                  size="sm"
                  variant="secondary"
                  onClick={() => patchAssembly(form, {
                    bom: [...bom, {
                      id: makeId('bom'),
                      label: form?.values?.name || `${t('elements.assembly.item', 'Item')} ${bom.length + 1}`,
                      quantity: 1,
                      unit: 'piece',
                      unitPrice: Number(form?.values?.price ?? 0) || 0,
                    }],
                  })}
                >
                  {t('elements.assembly.addItem', 'Add item')}
                </Button>
              </div>
              {bom.map((item: AsukaAssemblyBomItemDefinition, index: number) => (
                <div key={item.id || index} className="ask-flex ask-flex-col ask-gap-3 ask-rounded-xl ask-bg-surfaceAlt ask-p-3">
                  <ItemHeader
                    title={item.label || item.id || `${t('elements.assembly.item', 'Item')} ${index + 1}`}
                    removeLabel={t('common.remove', 'Remove')}
                    onRemove={() => patchAssembly(form, { bom: removeItem(bom, index) })}
                  />
                  <FormGrid columns={4} className="max-lg:ask-grid-cols-2">
                    <TextField
                      label={t('elements.assembly.sku', 'SKU')}
                      value={item.sku ?? ''}
                      onValueChange={(sku) => patchAssembly(form, { bom: updateItem(bom, index, { sku }) })}
                    />
                    <TextField
                      label={t('common.label', 'Label')}
                      value={item.label}
                      onValueChange={(label) => patchAssembly(form, { bom: updateItem(bom, index, { label }) })}
                    />
                    <NumberField
                      label={t('elements.assembly.quantity', 'Quantity')}
                      min={0}
                      step={0.01}
                      value={item.quantity ?? 1}
                      onValueChange={(quantity) => patchAssembly(form, { bom: updateItem(bom, index, { quantity: quantity ?? 1 }) })}
                    />
                    <NumberField
                      label={t('elements.assembly.unitPrice', 'Unit price')}
                      min={0}
                      step={0.01}
                      value={item.unitPrice ?? 0}
                      onValueChange={(unitPrice) => patchAssembly(form, { bom: updateItem(bom, index, { unitPrice: unitPrice ?? 0 }) })}
                    />
                  </FormGrid>
                </div>
              ))}
            </div>
          </>
        ) : null}
      </div>
    </SectionCard>
  );
};
