import { useMemo, useState } from "react";
import { createPortal } from "react-dom";
import type { ApiSettings } from "../../../types";
import { Button } from "@govoplan/core-webui";
import { Dialog } from "@govoplan/core-webui";
import { usePlatformUiCapability, type FilesFileExplorerUiCapability, type FilesManagedAttachmentSelection } from "@govoplan/core-webui";
import { DataGrid, DataGridEmptyAction, DataGridRowActions, type DataGridColumn } from "@govoplan/core-webui";
import { ToggleSwitch } from "@govoplan/core-webui";
import { getBool, getText } from "../utils/draftEditor";
import { attachmentRuleZipSelection, createAttachmentRule, nextAttachmentLabel, summarizeAttachmentRules, type AttachmentBasePath, type AttachmentRule, type AttachmentZipCollection } from "../utils/attachments";
import { insertAfter, moveArrayItem, i18nMessage } from "@govoplan/core-webui";
import { asRecord } from "../utils/campaignView";
import { renderTemplatePreviewText } from "../utils/templatePlaceholders";

export type { AttachmentBasePath, AttachmentRule } from "../utils/attachments";

type AttachmentRulesOverlayProps = {
  title: string;
  rules: AttachmentRule[];
  settings: ApiSettings;
  campaignId: string;
  disabled?: boolean;
  buttonLabel?: string;
  emptyText?: string;
  basePaths?: AttachmentBasePath[];
  zipConfig?: AttachmentZipCollection;
  filesModuleInstalled?: boolean;
  previewContext?: Record<string, string>;
  onChange: (rules: AttachmentRule[]) => void;
};

type AttachmentRulesTableProps = {
  rules: AttachmentRule[];
  settings: ApiSettings;
  campaignId: string;
  disabled?: boolean;
  emptyText?: string;
  basePaths?: AttachmentBasePath[];
  id?: string;
  activeChooserRuleIndex?: number | null;
  zipConfig?: AttachmentZipCollection;
  filesModuleInstalled?: boolean;
  previewContext?: Record<string, string>;
  onOpenFileChooser?: (ruleIndex: number) => void;
  onChange: (rules: AttachmentRule[]) => void;
};

type FileChooserState = {
  ruleIndex: number;
  basePath: AttachmentBasePath | null;
};

export default function AttachmentRulesOverlay({
  title,
  rules,
  settings,
  campaignId,
  disabled = false,
  buttonLabel,
  emptyText = "i18n:govoplan-campaign.no_attachment_files_or_matching_rules_configured.c6948b49",
  basePaths = [],
  zipConfig = { enabled: false, archives: [] },
  filesModuleInstalled = false,
  previewContext,
  onChange
}: AttachmentRulesOverlayProps) {
  const [open, setOpen] = useState(false);
  const [draftRules, setDraftRules] = useState<AttachmentRule[]>([]);
  const summary = useMemo(() => summarizeAttachmentRules(rules), [rules]);
  const label = buttonLabel ?? `direct: ${summary.direct} / rules: ${summary.rules}`;

  function openOverlay() {
    setDraftRules(cloneAttachmentRules(rules));
    setOpen(true);
  }

  function cancelOverlay() {
    setOpen(false);
  }

  function saveOverlay() {
    if (disabled) return;
    onChange(draftRules);
    setOpen(false);
  }

  const dialog = open ? createPortal(
    <Dialog
      open
      title={title}
      className="attachment-rules-modal"
      bodyClassName="attachment-rules-body"
      onClose={cancelOverlay}
      footer={
      <>
          <Button onClick={cancelOverlay}>i18n:govoplan-campaign.cancel.77dfd213</Button>
          <Button variant="primary" onClick={saveOverlay} disabled={disabled}>i18n:govoplan-campaign.save.efc007a3</Button>
        </>
      }>
      
      <AttachmentRulesTable
        rules={draftRules}
        settings={settings}
        campaignId={campaignId}
        disabled={disabled}
        emptyText={emptyText}
        basePaths={basePaths}
        zipConfig={zipConfig}
        filesModuleInstalled={filesModuleInstalled}
        previewContext={previewContext}
        activeChooserRuleIndex={null}
        onChange={setDraftRules} />
      
    </Dialog>,
    document.body
  ) : null;

  return (
    <>
      <Button className="attachment-summary-button" onClick={openOverlay} disabled={disabled && rules.length === 0} title={i18nMessage("i18n:govoplan-campaign.value_direct_file_s_value_rule_s_pattern_s.df9b46d9", { value0: summary.direct, value1: summary.rules })}>
        {label}
      </Button>
      {dialog}
    </>);

}

function cloneAttachmentRules(rules: AttachmentRule[]): AttachmentRule[] {
  return rules.map((rule) => ({
    ...rule,
    ...(rule.zip && typeof rule.zip === "object" ? { zip: { ...asRecord(rule.zip) } } : {})
  }));
}

export function AttachmentRulesTable({
  onChange,
  ...tableProps
}: AttachmentRulesTableProps) {
  return (
    <div className="attachment-rules-editor">
      <div className="attachment-rules-main">
        <AttachmentRulesDataGrid {...tableProps} onChange={onChange} />
      </div>
    </div>);

}

export function AttachmentRulesDataGrid({
  rules,
  settings,
  campaignId,
  disabled = false,
  emptyText = "i18n:govoplan-campaign.no_attachment_files_or_matching_rules_configured.c6948b49",
  basePaths = [],
  id = "attachment-rules",
  activeChooserRuleIndex = null,
  zipConfig = { enabled: false, archives: [] },
  filesModuleInstalled = false,
  previewContext,
  onOpenFileChooser,
  onChange
}: AttachmentRulesTableProps) {
  const [fileChooser, setFileChooser] = useState<FileChooserState | null>(null);
  const filesFileExplorer = usePlatformUiCapability<FilesFileExplorerUiCapability>("files.fileExplorer");
  const ManagedFileChooser = filesModuleInstalled ? filesFileExplorer?.ManagedFileChooser : null;
  const managedFilesAvailable = Boolean(ManagedFileChooser);

  function patchRule(index: number, patch: Partial<AttachmentRule>) {
    onChange(rules.map((rule, currentIndex) => currentIndex === index ? { ...rule, ...patch } : rule));
  }

  function addRule(afterIndex = rules.length - 1) {
    if (disabled || basePaths.length === 0) return;
    const nextRule = createAttachmentRule(basePaths[0].path, nextAttachmentLabel(rules), basePaths[0].id);
    onChange(insertAfter(rules, afterIndex, nextRule));
  }

  function removeRule(index: number) {
    setFileChooser(null);
    onChange(rules.filter((_, currentIndex) => currentIndex !== index));
  }

  function moveRule(index: number, targetIndex: number) {
    if (disabled || index === targetIndex) return;
    setFileChooser(null);
    onChange(moveArrayItem(rules, index, targetIndex));
  }

  function openFileChooser(ruleIndex: number) {
    if (!managedFilesAvailable) return;
    if (onOpenFileChooser) {
      onOpenFileChooser(ruleIndex);
      return;
    }
    const rule = rules[ruleIndex] ?? {};
    const currentPath = getText(rule, "base_dir");
    const currentBasePathId = getText(rule, "base_path_id");
    const explicitlyReferenced = Boolean(currentBasePathId || currentPath);
    const basePath = basePaths.find((item) => item.id === currentBasePathId) ??
    basePaths.find((item) => item.path === currentPath) ?? (
    !explicitlyReferenced ? basePaths[0] : undefined) ??
    null;
    if (!basePath) return;
    setFileChooser({ ruleIndex, basePath });
  }

  function selectAttachment(selection: FilesManagedAttachmentSelection) {
    if (!fileChooser) return;
    const currentRule = rules[fileChooser.ruleIndex] ?? {};
    patchRule(fileChooser.ruleIndex, {
      base_path_id: fileChooser.basePath?.id ?? "",
      base_dir: fileChooser.basePath?.path ?? (selection.folderPath || "."),
      file_filter: selection.fileFilter,
      type: selection.selectionType === "file" ? "direct" : "pattern",
      include_subdirs: false,
      label: getText(currentRule, "label") || `Attachment ${fileChooser.ruleIndex + 1}`
    });
    setFileChooser(null);
  }

  return (
    <>
      <DataGrid
        id={id}
        rows={rules}
        columns={attachmentRuleColumns({ disabled, rules, basePaths, zipConfig, filesModuleInstalled: managedFilesAvailable, activeChooserRuleIndex: activeChooserRuleIndex ?? fileChooser?.ruleIndex ?? null, patchRule, addRule, moveRule, openFileChooser, removeRule })}
        getRowKey={(rule, index) => String(rule.id ?? index)}
        emptyText={basePaths.length === 0 ? "i18n:govoplan-campaign.no_attachment_source_is_enabled_for_individual_a.818a2820" : emptyText}
        emptyAction={<DataGridEmptyAction onAdd={() => addRule(-1)} disabled={disabled || basePaths.length === 0} label="i18n:govoplan-campaign.add_first_attachment.025fbf31" />}
        className="attachment-rules-table-wrap attachment-rules-table"
        rowClassName={(_rule, index) => (activeChooserRuleIndex ?? fileChooser?.ruleIndex) === index ? "is-choosing-file" : undefined} />
      
      {fileChooser && ManagedFileChooser &&
      <ManagedFileChooser
        open
        settings={settings}
        storageScope={campaignId}
        linkTarget={{ type: "campaign", id: campaignId, label: "campaign" }}
        mode="attachment"
        source={fileChooser.basePath?.source}
        basePath={fileChooser.basePath?.path ?? "."}
        initialPattern={getText(rules[fileChooser.ruleIndex], "file_filter")}
        rememberKey={`${id}:${String(rules[fileChooser.ruleIndex]?.id ?? fileChooser.ruleIndex)}`}
        previewContext={previewContext}
        renderPatternPreview={(pattern, context) => renderTemplatePreviewText(pattern, context, false)}
        onClose={() => setFileChooser(null)}
        onSelectAttachment={selectAttachment} />

      }
    </>);

}

type AttachmentRuleColumnContext = {
  disabled: boolean;
  rules: AttachmentRule[];
  basePaths: AttachmentBasePath[];
  zipConfig: AttachmentZipCollection;
  filesModuleInstalled: boolean;
  activeChooserRuleIndex: number | null;
  patchRule: (index: number, patch: Partial<AttachmentRule>) => void;
  addRule: (afterIndex?: number) => void;
  moveRule: (index: number, targetIndex: number) => void;
  openFileChooser: (ruleIndex: number) => void;
  removeRule: (index: number) => void;
};

function attachmentRuleColumns({ disabled, rules, basePaths, zipConfig, filesModuleInstalled, activeChooserRuleIndex: _activeChooserRuleIndex, patchRule, addRule, moveRule, openFileChooser, removeRule }: AttachmentRuleColumnContext): DataGridColumn<AttachmentRule>[] {
  return [
  { id: "label", header: "i18n:govoplan-campaign.label.74341e3c", width: 190, resizable: true, sortable: true, filterable: true, sticky: "start", render: (rule, index) => <input value={getText(rule, "label")} disabled={disabled} placeholder="i18n:govoplan-campaign.attachment_label.a340f70e" onChange={(event) => patchRule(index, { label: event.target.value })} />, value: (rule) => getText(rule, "label") },
  {
    id: "base_path",
    header: "i18n:govoplan-campaign.base_path.6a4867ca",
    width: 250,
    sortable: true,
    filterable: true,
    columnType: "from-list",
    list: { options: basePaths.map((basePath) => ({ value: basePath.path, label: basePath.name || basePath.path })) },
    render: (rule, index) => {
      const currentBasePathValue = getText(rule, "base_dir");
      const currentBasePathId = getText(rule, "base_path_id");
      const explicitlyReferenced = Boolean(currentBasePathId || currentBasePathValue);
      const selectedBasePath = basePaths.find((basePath) => basePath.id === currentBasePathId) ??
      basePaths.find((basePath) => basePath.path === currentBasePathValue) ?? (
      !explicitlyReferenced ? basePaths[0] : undefined);
      return (
        <select
          value={selectedBasePath?.id ?? ""}
          disabled={disabled || basePaths.length === 0}
          onChange={(event) => {
            const nextBasePath = basePaths.find((basePath) => basePath.id === event.target.value);
            if (nextBasePath) patchRule(index, { base_path_id: nextBasePath.id, base_dir: nextBasePath.path });
          }}>
          
            {!selectedBasePath &&
          <option value="" disabled>{basePaths.length === 0 ? "i18n:govoplan-campaign.no_individual_attachment_source.6b54176a" : "i18n:govoplan-campaign.unavailable_attachment_source.7ff1096e"}</option>
          }
            {basePaths.map((basePath) => <option key={basePath.id} value={basePath.id}>{basePath.name || basePath.path}</option>)}
          </select>);

    },
    value: (rule) => getText(rule, "base_dir", basePaths[0]?.path ?? "")
  },
  {
    id: "file_filter",
    header: "i18n:govoplan-campaign.file_pattern.86320b07",
    width: "minmax(260px, 1fr)",
    maxWidth: 640,
    resizable: true,
    sortable: true,
    filterable: true,
    render: (rule, index) =>
    <div className="field-with-action split-field-action">
          <input
        className="chooser-display-input"
        value={getText(rule, "file_filter")}
        disabled={disabled || basePaths.length === 0}
        readOnly={filesModuleInstalled}
        tabIndex={filesModuleInstalled ? -1 : undefined}
        placeholder={filesModuleInstalled ? "i18n:govoplan-campaign.choose_a_managed_file_or_pattern.96bb3bfb" : "file.pdf or **/*.pdf"}
        onChange={(event) => {
          if (!filesModuleInstalled) patchRule(index, { file_filter: event.target.value });
        }}
        onClick={() => filesModuleInstalled && !disabled && openFileChooser(index)}
        onKeyDown={(event) => {
          if (filesModuleInstalled && !disabled && (event.key === "Enter" || event.key === " ")) {
            event.preventDefault();
            openFileChooser(index);
          }
        }} />
      
          {filesModuleInstalled && <Button onClick={() => openFileChooser(index)} disabled={disabled || basePaths.length === 0}>i18n:govoplan-campaign.choose.78b7c9f6</Button>}
        </div>,

    value: (rule) => getText(rule, "file_filter")
  },
  {
    id: "message_filename_template",
    header: "i18n:govoplan-campaign.message_filename_template.0b7ac934",
    width: 230,
    resizable: true,
    sortable: true,
    filterable: true,
    render: (rule, index) =>
    <input
        value={getText(rule, "message_filename_template")}
        disabled={disabled}
        placeholder="i18n:govoplan-campaign.keep_original_filename.9b805045"
        onChange={(event) => patchRule(index, { message_filename_template: event.target.value })} />,

    value: (rule) => getText(rule, "message_filename_template")
  },
  {
    id: "zip_entry_name_template",
    header: "i18n:govoplan-campaign.zip_entry_name_template.83772a73",
    width: 230,
    resizable: true,
    sortable: true,
    filterable: true,
    render: (rule, index) =>
    <input
        value={getText(rule, "zip_entry_name_template")}
        disabled={disabled}
        placeholder="i18n:govoplan-campaign.keep_original_zip_entry_name.d51558c4"
        onChange={(event) => patchRule(index, { zip_entry_name_template: event.target.value })} />,

    value: (rule) => getText(rule, "zip_entry_name_template")
  },
  ...(zipConfig.enabled ? [{
    id: "zip",
    header: "i18n:govoplan-campaign.zip_archive.5a2430dd",
    width: 240,
    sortable: true,
    filterable: true,
    columnType: "from-list",
    list: {
      options: [
      { value: "exclude", label: "i18n:govoplan-campaign.exclude_from_zip.a6422da1" },
      { value: "inherit", label: "i18n:govoplan-campaign.campaign_standard.c88ce44c" },
      ...zipConfig.archives.map((archive) => ({ value: archive.id, label: archive.name }))]

    },
    render: (rule: AttachmentRule, index: number) => {
      const selection = attachmentRuleZipSelection(rule);
      return (
        <select
          value={selection}
          disabled={disabled || zipConfig.archives.length === 0}
          aria-label={i18nMessage("i18n:govoplan-campaign.zip_archive_for_value.3dfaf812", { value0: getText(rule, "label") || `attachment ${index + 1}` })}
          onChange={(event) => patchRule(index, { zip: { ...asRecord(rule.zip), archive_id: event.target.value } })}>
          
            <option value="exclude">i18n:govoplan-campaign.exclude_from_zip.a6422da1</option>
            <option value="inherit">i18n:govoplan-campaign.campaign_standard.c88ce44c</option>
            {zipConfig.archives.map((archive) => <option key={archive.id} value={archive.id}>{archive.name}</option>)}
          </select>);

    },
    value: (rule: AttachmentRule) => attachmentRuleZipSelection(rule)
  } satisfies DataGridColumn<AttachmentRule>] : []),
  { id: "required", header: "i18n:govoplan-campaign.required.eed6bfb4", width: 175, sortable: true, filterable: true, columnType: "from-list", list: { options: [{ value: "required", label: "i18n:govoplan-campaign.required.eed6bfb4" }, { value: "optional", label: "i18n:govoplan-campaign.optional.0c6c4102" }] }, render: (rule, index) => <ToggleSwitch label="i18n:govoplan-campaign.required.eed6bfb4" checked={getBool(rule, "required", true)} disabled={disabled} onChange={(checked) => patchRule(index, { required: checked })} />, value: (rule) => getBool(rule, "required", true) ? "required" : "optional" },
  {
    id: "actions",
    header: "i18n:govoplan-campaign.actions.c3cd636a",
    width: 180,
    sticky: "end",
    render: (_rule, index) =>
    <DataGridRowActions
      disabled={disabled}
      onAddBelow={() => addRule(index)}
      onRemove={() => removeRule(index)}
      onMoveUp={index > 0 ? () => moveRule(index, index - 1) : undefined}
      onMoveDown={index < rules.length - 1 ? () => moveRule(index, index + 1) : undefined}
      addLabel="i18n:govoplan-campaign.add_attachment_below.c7b66ffd"
      removeLabel="i18n:govoplan-campaign.remove_attachment.0fcc6594"
      moveUpLabel="i18n:govoplan-campaign.move_attachment_up.28614804"
      moveDownLabel="i18n:govoplan-campaign.move_attachment_down.a4d6604f" />


  }];

}
