"use client";

import { Plus, Trash2, X } from "lucide-react";
import { Button, Field, Input, Select, Textarea } from "@/components/ui/kit";
import { ContentEditor, type AnyContent } from "@/components/bots/content-editor";
import { PreviewFrame } from "@/components/templates/template-preview";
import { ReplyBubble } from "@/components/bots/reply-preview";
import type { BotAsset, CustomField, FlowIssue, FlowNodeType, Group, ReplyType, Tag, TeamMember } from "@/lib/types";
import { NODE_META } from "./node-meta";

export interface Lookups {
  customFields: CustomField[];
  groups: Group[];
  tags: Tag[];
  team: TeamMember[];
}

const CONTACT_FIELDS: [string, string][] = [
  ["first_name", "First name"],
  ["last_name", "Last name"],
  ["email", "Email"],
  ["phone", "WhatsApp number"],
  ["country", "Country code"],
  ["language_code", "Language code"],
  ["opt_in_status", "Marketing consent"],
];
const WRITABLE_FIELDS: [string, string][] = [
  ["first_name", "First name"],
  ["last_name", "Last name"],
  ["email", "Email"],
  ["country", "Country code"],
  ["language_code", "Language code"],
];

type Data = Record<string, unknown>;
type Rule = { field: string; operator: string; value: string };
type Option = { id?: string; label: string; match: string; value: string };
type UpdateAction = { type: string; field?: string; value?: string; tag_id?: number; group_id?: number };

function ErrorList({ issues }: { issues: FlowIssue[] }) {
  const general = issues.filter((i) => !i.field);
  if (!general.length) return null;
  return (
    <ul className="space-y-1.5">
      {general.map((i, k) => (
        <li key={k} className={i.severity === "error" ? "rounded-md bg-brick-soft px-3 py-2 text-[13px] text-brick" : "rounded-md bg-amber-soft px-3 py-2 text-[13px] text-amber"}>
          {i.message}
        </li>
      ))}
    </ul>
  );
}

function ChoiceEditor({ data, set, errors, lookups }: { data: Data; set: (p: Data) => void; errors: Record<string, string>; lookups: Lookups }) {
  const options = (data.options as Option[]) ?? [];
  const setOpt = (i: number, patch: Partial<Option>) => set({ options: options.map((o, j) => (j === i ? { ...o, ...patch } : o)) });
  return (
    <div className="space-y-4">
      <Field label="Question to send (optional)" htmlFor="pp-prompt" error={errors.prompt} hint="Leave empty if the previous message already asked it.">
        <Textarea id="pp-prompt" rows={3} value={String(data.prompt ?? "")} onChange={(e) => set({ prompt: e.target.value })} />
      </Field>
      <fieldset className="space-y-3">
        <legend className="mb-1 text-[13px] font-medium">Answers to recognise</legend>
        {errors.options && <p className="text-[13px] text-brick">{errors.options}</p>}
        {options.map((o, i) => (
          <div key={o.id ?? i} className="space-y-2 rounded-lg border border-line p-3">
            <div className="flex gap-2">
              <Input aria-label={`Answer ${i + 1} name`} placeholder="Name, e.g. Android" value={o.label} maxLength={40} onChange={(e) => setOpt(i, { label: e.target.value })} invalid={!!errors[`options.${i}.label`]} />
              <button type="button" disabled={options.length === 1} onClick={() => set({ options: options.filter((_, j) => j !== i) })} className="rounded p-1.5 text-muted hover:bg-paper hover:text-brick disabled:opacity-30" aria-label={`Remove answer ${i + 1}`}>
                <Trash2 className="size-4" />
              </button>
            </div>
            <div className="grid grid-cols-[130px_1fr] gap-2">
              <Select aria-label={`Answer ${i + 1} match`} value={o.match} onChange={(e) => setOpt(i, { match: e.target.value })}>
                <option value="is">Is exactly</option>
                <option value="starts_with">Starts with</option>
                <option value="contains_word">Has the word</option>
                <option value="contains">Contains</option>
              </Select>
              <Input aria-label={`Answer ${i + 1} text`} placeholder="What they type" value={o.value} maxLength={100} onChange={(e) => setOpt(i, { value: e.target.value })} invalid={!!errors[`options.${i}.value`]} />
            </div>
            {(errors[`options.${i}.label`] || errors[`options.${i}.value`]) && <p className="text-[13px] text-brick">{errors[`options.${i}.label`] ?? errors[`options.${i}.value`]}</p>}
          </div>
        ))}
        {options.length < 10 && (
          <button type="button" onClick={() => set({ options: [...options, { label: "", match: "contains_word", value: "" }] })} className="inline-flex items-center gap-1 text-[13px] font-medium text-teal hover:underline">
            <Plus className="size-4" /> Add answer
          </button>
        )}
      </fieldset>
      <Field label="Save their answer to (optional)" htmlFor="pp-save">
        <Select id="pp-save" value={String(data.save_to ?? "")} onChange={(e) => set({ save_to: e.target.value || undefined })}>
          <option value="">Don&apos;t save</option>
          {WRITABLE_FIELDS.map(([k, l]) => (
            <option key={k} value={k}>
              {l}
            </option>
          ))}
          {lookups.customFields.map((f) => (
            <option key={f.key} value={`custom:${f.key}`}>
              {f.label}
            </option>
          ))}
        </Select>
      </Field>
    </div>
  );
}

const OPERATORS: Record<string, [string, string][]> = {
  value: [
    ["equals", "is"],
    ["not_equals", "is not"],
    ["contains", "contains"],
    ["starts_with", "starts with"],
    ["is_empty", "is empty"],
    ["is_not_empty", "is not empty"],
  ],
  group: [
    ["in_group", "is in"],
    ["not_in_group", "is not in"],
  ],
  tag: [
    ["has_tag", "has"],
    ["not_has_tag", "does not have"],
  ],
};

function ConditionEditor({ data, set, errors, lookups }: { data: Data; set: (p: Data) => void; errors: Record<string, string>; lookups: Lookups }) {
  const rules = (data.rules as Rule[]) ?? [];
  const setRule = (i: number, patch: Partial<Rule>) => set({ rules: rules.map((r, j) => (j === i ? { ...r, ...patch } : r)) });
  return (
    <div className="space-y-4">
      <Field label="Go down “Yes” when" htmlFor="pp-match">
        <Select id="pp-match" value={String(data.match ?? "all")} onChange={(e) => set({ match: e.target.value })}>
          <option value="all">All rules are true</option>
          <option value="any">Any rule is true</option>
        </Select>
      </Field>
      {errors.rules && <p className="text-[13px] text-brick">{errors.rules}</p>}
      {rules.map((r, i) => {
        const kind = r.field === "group" ? "group" : r.field === "tag" ? "tag" : "value";
        const err = errors[`rules.${i}.value`] ?? errors[`rules.${i}.operator`] ?? errors[`rules.${i}.field`];
        return (
          <div key={i} className="space-y-2 rounded-lg border border-line p-3">
            <div className="flex gap-2">
              <Select
                aria-label={`Rule ${i + 1} field`}
                value={r.field}
                onChange={(e) => {
                  const f = e.target.value;
                  const k = f === "group" ? "group" : f === "tag" ? "tag" : "value";
                  setRule(i, { field: f, operator: OPERATORS[k][0][0], value: "" });
                }}
              >
                <optgroup label="Contact">
                  {CONTACT_FIELDS.map(([k, l]) => (
                    <option key={k} value={k}>
                      {l}
                    </option>
                  ))}
                </optgroup>
                {lookups.customFields.length > 0 && (
                  <optgroup label="Custom fields">
                    {lookups.customFields.map((f) => (
                      <option key={f.key} value={`custom:${f.key}`}>
                        {f.label}
                      </option>
                    ))}
                  </optgroup>
                )}
                <optgroup label="Other">
                  <option value="group">Group</option>
                  <option value="tag">Tag</option>
                  <option value="last_reply">Their last message</option>
                </optgroup>
              </Select>
              <button type="button" disabled={rules.length === 1} onClick={() => set({ rules: rules.filter((_, j) => j !== i) })} className="rounded p-1.5 text-muted hover:bg-paper hover:text-brick disabled:opacity-30" aria-label={`Remove rule ${i + 1}`}>
                <Trash2 className="size-4" />
              </button>
            </div>
            <div className="grid grid-cols-[130px_1fr] gap-2">
              <Select aria-label={`Rule ${i + 1} check`} value={r.operator} onChange={(e) => setRule(i, { operator: e.target.value })}>
                {OPERATORS[kind].map(([k, l]) => (
                  <option key={k} value={k}>
                    {l}
                  </option>
                ))}
              </Select>
              {kind === "group" || kind === "tag" ? (
                <Select aria-label={`Rule ${i + 1} ${kind}`} value={r.value} onChange={(e) => setRule(i, { value: e.target.value })} invalid={!!errors[`rules.${i}.value`]}>
                  <option value="">Choose a {kind}</option>
                  {(kind === "group" ? lookups.groups : lookups.tags).map((g) => (
                    <option key={g.id} value={g.id}>
                      {g.name}
                    </option>
                  ))}
                </Select>
              ) : r.field === "opt_in_status" ? (
                <Select aria-label={`Rule ${i + 1} value`} value={r.value} onChange={(e) => setRule(i, { value: e.target.value })} disabled={r.operator.includes("empty")}>
                  <option value="">Choose</option>
                  <option value="opted_in">Opted in</option>
                  <option value="opted_out">Opted out</option>
                  <option value="unknown">Not asked</option>
                </Select>
              ) : (
                <Input aria-label={`Rule ${i + 1} value`} value={r.value} disabled={r.operator.includes("empty")} onChange={(e) => setRule(i, { value: e.target.value })} invalid={!!errors[`rules.${i}.value`]} placeholder={r.field === "country" ? "e.g. IN" : ""} />
              )}
            </div>
            {err && <p className="text-[13px] text-brick">{err}</p>}
          </div>
        );
      })}
      {rules.length < 10 && (
        <button type="button" onClick={() => set({ rules: [...rules, { field: "first_name", operator: "is_not_empty", value: "" }] })} className="inline-flex items-center gap-1 text-[13px] font-medium text-teal hover:underline">
          <Plus className="size-4" /> Add rule
        </button>
      )}
      <p className="text-[13px] text-muted">Text checks ignore upper and lower case.</p>
    </div>
  );
}

function UpdateEditor({ data, set, errors, lookups }: { data: Data; set: (p: Data) => void; errors: Record<string, string>; lookups: Lookups }) {
  const actions = (data.actions as UpdateAction[]) ?? [];
  const setAct = (i: number, a: UpdateAction) => set({ actions: actions.map((x, j) => (j === i ? a : x)) });
  return (
    <div className="space-y-3">
      {errors.actions && <p className="text-[13px] text-brick">{errors.actions}</p>}
      {actions.map((a, i) => {
        const err = Object.entries(errors).find(([k]) => k.startsWith(`actions.${i}.`))?.[1];
        return (
          <div key={i} className="space-y-2 rounded-lg border border-line p-3">
            <div className="flex gap-2">
              <Select
                aria-label={`Change ${i + 1}`}
                value={a.type}
                onChange={(e) => {
                  const t = e.target.value;
                  setAct(i, t === "set_field" ? { type: t, field: "first_name", value: "" } : t === "set_opt_in" ? { type: t, value: "opted_in" } : { type: t });
                }}
              >
                <option value="set_field">Set a field</option>
                <option value="add_tag">Add a tag</option>
                <option value="remove_tag">Remove a tag</option>
                <option value="add_group">Add to a group</option>
                <option value="remove_group">Remove from a group</option>
                <option value="set_opt_in">Set marketing consent</option>
              </Select>
              <button type="button" disabled={actions.length === 1} onClick={() => set({ actions: actions.filter((_, j) => j !== i) })} className="rounded p-1.5 text-muted hover:bg-paper hover:text-brick disabled:opacity-30" aria-label={`Remove change ${i + 1}`}>
                <Trash2 className="size-4" />
              </button>
            </div>
            {a.type === "set_field" && (
              <div className="grid grid-cols-2 gap-2">
                <Select aria-label="Field" value={a.field} onChange={(e) => setAct(i, { ...a, field: e.target.value })}>
                  {WRITABLE_FIELDS.map(([k, l]) => (
                    <option key={k} value={k}>
                      {l}
                    </option>
                  ))}
                  {lookups.customFields.map((f) => (
                    <option key={f.key} value={`custom:${f.key}`}>
                      {f.label}
                    </option>
                  ))}
                </Select>
                <Input aria-label="Value" placeholder="Value, e.g. {last_reply}" value={a.value ?? ""} onChange={(e) => setAct(i, { ...a, value: e.target.value })} />
              </div>
            )}
            {(a.type === "add_tag" || a.type === "remove_tag") && (
              <Select aria-label="Tag" value={a.tag_id ?? ""} onChange={(e) => setAct(i, { ...a, tag_id: Number(e.target.value) })}>
                <option value="">Choose a tag</option>
                {lookups.tags.map((t) => (
                  <option key={t.id} value={t.id}>
                    {t.name}
                  </option>
                ))}
              </Select>
            )}
            {(a.type === "add_group" || a.type === "remove_group") && (
              <Select aria-label="Group" value={a.group_id ?? ""} onChange={(e) => setAct(i, { ...a, group_id: Number(e.target.value) })}>
                <option value="">Choose a group</option>
                {lookups.groups.map((g) => (
                  <option key={g.id} value={g.id}>
                    {g.name}
                  </option>
                ))}
              </Select>
            )}
            {a.type === "set_opt_in" && (
              <Select aria-label="Consent" value={a.value} onChange={(e) => setAct(i, { ...a, value: e.target.value })}>
                <option value="opted_in">Opted in to marketing</option>
                <option value="opted_out">Opted out of marketing</option>
              </Select>
            )}
            {err && <p className="text-[13px] text-brick">{err}</p>}
          </div>
        );
      })}
      {actions.length < 20 && (
        <button type="button" onClick={() => set({ actions: [...actions, { type: "set_field", field: "first_name", value: "" }] })} className="inline-flex items-center gap-1 text-[13px] font-medium text-teal hover:underline">
          <Plus className="size-4" /> Add change
        </button>
      )}
      <p className="text-[13px] text-muted">Consent set here is recorded with the source “bot flow”, so only use it when the customer clearly agreed, e.g. by tapping a Yes button.</p>
    </div>
  );
}

export function PropertiesPanel({
  nodeId,
  type,
  data,
  issues,
  lookups,
  assets,
  trigger,
  readOnly,
  onChange,
  onAsset,
  onClose,
  onEditSettings,
}: {
  nodeId: string;
  type: FlowNodeType;
  data: Data;
  issues: FlowIssue[];
  lookups: Lookups;
  assets: Record<number, BotAsset>;
  trigger: string;
  readOnly: boolean;
  onChange: (data: Data) => void;
  onAsset: (a: BotAsset) => void;
  onClose: () => void;
  onEditSettings: () => void;
}) {
  const meta = NODE_META[type];
  const set = (patch: Data) => onChange({ ...data, ...patch });
  const errors = Object.fromEntries(issues.filter((i) => i.field).map((i) => [i.field!, i.message]));
  const isMessage = type === "text" || type === "media" || type === "interactive";

  return (
    <div className="flex h-full min-h-0 flex-col" aria-label={`${meta.label} settings`}>
      <header className="flex items-center gap-2 border-b border-line px-4 py-3">
        <span className={`grid size-7 place-items-center rounded-md [&_svg]:size-4 ${meta.accent}`}>{meta.icon}</span>
        <h2 className="min-w-0 flex-1 truncate font-semibold">{meta.label}</h2>
        <button onClick={onClose} className="rounded p-1 text-muted hover:bg-paper" aria-label="Close step settings">
          <X className="size-5" />
        </button>
      </header>
      <fieldset disabled={readOnly} className="min-h-0 flex-1 space-y-5 overflow-y-auto p-4">
        <ErrorList issues={issues} />
        {type !== "start" && (
          <Field label="Step name" htmlFor="pp-label" hint="Shown on the canvas" error={errors.label}>
            <Input id="pp-label" value={String(data.label ?? "")} maxLength={60} placeholder={meta.label} onChange={(e) => set({ label: e.target.value })} />
          </Field>
        )}
        {type === "start" && (
          <div className="space-y-3 text-sm">
            <p>
              This flow starts when <span className="font-semibold">{trigger}</span>.
            </p>
            <Button size="sm" onClick={onEditSettings}>
              Change trigger
            </Button>
          </div>
        )}
        {isMessage && (
          <>
            <ContentEditor
              key={nodeId}
              idPrefix={`pp-${nodeId}`}
              type={type as ReplyType}
              content={data as unknown as AnyContent}
              onChange={(c) => onChange({ ...(c as unknown as Data), label: data.label })}
              errors={errors}
              customFields={lookups.customFields}
              assets={assets}
              onAsset={onAsset}
            />
            <div>
              <p className="mb-2 text-[13px] font-medium text-muted">Preview</p>
              <PreviewFrame>
                <ReplyBubble type={type as ReplyType} content={data as unknown as AnyContent} assets={assets} />
              </PreviewFrame>
            </div>
          </>
        )}
        {type === "choice" && <ChoiceEditor data={data} set={set} errors={errors} lookups={lookups} />}
        {type === "condition" && <ConditionEditor data={data} set={set} errors={errors} lookups={lookups} />}
        {type === "delay" && (
          <div className="space-y-2">
            <div className="grid grid-cols-[1fr_140px] gap-2">
              <Field label="Wait" htmlFor="pp-amount" error={errors.amount}>
                <Input id="pp-amount" type="number" min={1} value={String(data.amount ?? "")} onChange={(e) => set({ amount: e.target.value === "" ? "" : Number(e.target.value) })} invalid={!!errors.amount} />
              </Field>
              <Field label="Unit" htmlFor="pp-unit">
                <Select id="pp-unit" value={String(data.unit ?? "seconds")} onChange={(e) => set({ unit: e.target.value })}>
                  <option value="seconds">Seconds</option>
                  <option value="minutes">Minutes</option>
                  <option value="hours">Hours</option>
                </Select>
              </Field>
            </div>
            <p className="text-[13px] text-muted">Up to 23 hours, so the next message still lands inside WhatsApp&apos;s 24-hour reply window.</p>
          </div>
        )}
        {type === "contact_update" && <UpdateEditor data={data} set={set} errors={errors} lookups={lookups} />}
        {type === "handoff" && (
          <div className="space-y-4">
            <Field label="Message to the customer (optional)" htmlFor="pp-hmsg" error={errors.message}>
              <Textarea id="pp-hmsg" rows={3} value={String(data.message ?? "")} onChange={(e) => set({ message: e.target.value })} />
            </Field>
            <Field label="Assign to" htmlFor="pp-assign">
              <Select id="pp-assign" value={String(data.assign_to ?? "")} onChange={(e) => set({ assign_to: e.target.value ? Number(e.target.value) : null })}>
                <option value="">Leave unassigned for the team</option>
                {lookups.team
                  .filter((m) => m.is_active)
                  .map((m) => (
                    <option key={m.id} value={m.id}>
                      {m.name}
                    </option>
                  ))}
              </Select>
            </Field>
            <Field label="Internal note (optional)" htmlFor="pp-note" hint="Added to the chat for your team; never sent to the customer.">
              <Textarea id="pp-note" rows={2} value={String(data.note ?? "")} onChange={(e) => set({ note: e.target.value })} />
            </Field>
            <p className="text-[13px] text-muted">Automated replies pause in this chat until a teammate switches them back on in the inbox.</p>
          </div>
        )}
        {type === "end" && (
          <Field label="Closing message (optional)" htmlFor="pp-endmsg" error={errors.message}>
            <Textarea id="pp-endmsg" rows={3} value={String(data.message ?? "")} onChange={(e) => set({ message: e.target.value })} />
          </Field>
        )}
      </fieldset>
    </div>
  );
}
