'use client';

/**
 * ActionAreaCard — the only interactive remediation surface on the phishing
 * campaign review page (REVIEW-05, REVIEW-06). Renders one checkbox row per
 * recommended action with an always-visible, editable params form
 * pre-filled via deriveDefaultParams(), and submits exactly
 * ApproveActionInput[] to the existing /approve route.
 *
 * D-04: every action refetches via onActionComplete() on success — this
 * component never optimistically mutates local campaign state.
 */

import { useEffect, useState, type ReactNode } from 'react';
import { Loader2, ShieldAlert } from 'lucide-react';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Checkbox } from '@/components/ui/checkbox';
import { Label } from '@/components/ui/label';
import { Input } from '@/components/ui/input';
import { Textarea } from '@/components/ui/textarea';
import { Button, buttonVariants } from '@/components/ui/button';
import {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogTitle,
} from '@/components/ui/alert-dialog';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
import { toast } from 'sonner';
import { useSession } from '@/lib/auth-client';
import { hasPermission } from '@/lib/permissions';
import { deriveDefaultParams } from '@/lib/services/remediation-default-params';

export interface RemediationActionSummary {
  id: string;
  actionType: string;
  status: string;
  completedAt: string | null;
  approvedBy: string | null;
}

export interface ActionAreaClassification {
  recommendedActions: string[];
}

export interface ActionAreaEvidence {
  requesterEmail: string | null;
  senderEmail: string | null;
  senderDomain: string | null;
  messageId: string | null;
}

interface ActionAreaCardProps {
  campaignId: string;
  classification: ActionAreaClassification | null;
  remediationActions: RemediationActionSummary[];
  campaignStatus: string;
  campaignUpdatedAt: string;
  evidence: ActionAreaEvidence;
  onActionComplete: () => void;
}

const ACTION_LABEL: Record<string, string> = {
  block_sender: 'Block sender',
  purge_message: 'Purge message',
  warn_user: 'Warn user',
  no_action: 'No action',
  reset_password: 'Reset password',
  isolate_endpoint: 'Isolate endpoint',
  disable_forwarding_rule: 'Disable forwarding rule',
  acknowledge_user: 'Acknowledge user',
};

function humanizeAction(actionType: string): string {
  return (
    ACTION_LABEL[actionType] ??
    actionType
      .split('_')
      .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
      .join(' ')
  );
}

interface ActionRowState {
  checked: boolean;
  params: Record<string, unknown>;
}

/**
 * Normalizes a row's editable params back into the submit shape. The only
 * field that needs conversion is purge_message's `mailboxes` — edited as a
 * comma-separated string in the form, submitted as `string[]` to match
 * ApproveActionInput exactly.
 */
function normalizeParamsForSubmit(actionType: string, params: Record<string, unknown>): Record<string, unknown> {
  if (actionType === 'purge_message' && typeof params.mailboxes === 'string') {
    return {
      ...params,
      mailboxes: params.mailboxes
        .split(',')
        .map((m) => m.trim())
        .filter(Boolean),
    };
  }
  return params;
}

function ActionParamsForm({
  actionType,
  params,
  onChange,
}: {
  actionType: string;
  params: Record<string, unknown>;
  onChange: (params: Record<string, unknown>) => void;
}) {
  function setField(key: string, value: string) {
    onChange({ ...params, [key]: value });
  }

  const str = (key: string) => (typeof params[key] === 'string' ? (params[key] as string) : '');

  switch (actionType) {
    case 'no_action':
      return (
        <p className="text-sm text-muted-foreground">
          No parameters — informational verdict, no remediation needed.
        </p>
      );
    case 'acknowledge_user':
      return (
        <p className="text-sm text-muted-foreground">
          No parameters — posts a customer-visible thank-you note to the reporting employee.
        </p>
      );
    case 'warn_user':
      return (
        <div className="grid gap-3 sm:grid-cols-2">
          <div className="space-y-1.5">
            <Label htmlFor={`${actionType}-recipientEmail`}>Recipient email</Label>
            <Input
              id={`${actionType}-recipientEmail`}
              value={str('recipientEmail')}
              onChange={(e) => setField('recipientEmail', e.target.value)}
            />
          </div>
          <div className="space-y-1.5 sm:col-span-2">
            <Label htmlFor={`${actionType}-message`}>Message</Label>
            <Textarea
              id={`${actionType}-message`}
              value={str('message')}
              onChange={(e) => setField('message', e.target.value)}
            />
          </div>
        </div>
      );
    case 'block_sender':
      return (
        <div className="grid gap-3 sm:grid-cols-2">
          <div className="space-y-1.5">
            <Label htmlFor={`${actionType}-senderEmail`}>Sender email</Label>
            <Input
              id={`${actionType}-senderEmail`}
              value={str('senderEmail')}
              onChange={(e) => setField('senderEmail', e.target.value)}
            />
          </div>
          <div className="space-y-1.5">
            <Label htmlFor={`${actionType}-senderDomain`}>Sender domain</Label>
            <Input
              id={`${actionType}-senderDomain`}
              value={str('senderDomain')}
              onChange={(e) => setField('senderDomain', e.target.value)}
            />
          </div>
        </div>
      );
    case 'purge_message':
      return (
        <div className="grid gap-3 sm:grid-cols-2">
          <div className="space-y-1.5">
            <Label htmlFor={`${actionType}-messageId`}>Message ID</Label>
            <Input
              id={`${actionType}-messageId`}
              value={str('messageId')}
              onChange={(e) => setField('messageId', e.target.value)}
            />
          </div>
          <div className="space-y-1.5">
            <Label htmlFor={`${actionType}-mailboxes`}>Mailboxes</Label>
            <Input
              id={`${actionType}-mailboxes`}
              value={str('mailboxes')}
              onChange={(e) => setField('mailboxes', e.target.value)}
            />
            <p className="text-xs text-muted-foreground">Enter affected mailboxes, comma-separated</p>
          </div>
        </div>
      );
    case 'reset_password':
      return (
        <div className="space-y-1.5">
          <Label htmlFor={`${actionType}-userPrincipalName`}>User principal name</Label>
          <Input
            id={`${actionType}-userPrincipalName`}
            value={str('userPrincipalName')}
            onChange={(e) => setField('userPrincipalName', e.target.value)}
          />
        </div>
      );
    case 'isolate_endpoint':
      return (
        <div className="space-y-1.5">
          <Label htmlFor={`${actionType}-deviceId`}>Device ID</Label>
          <Input
            id={`${actionType}-deviceId`}
            value={str('deviceId')}
            onChange={(e) => setField('deviceId', e.target.value)}
          />
          <p className="text-xs text-muted-foreground">
            No device identifier available from evidence — enter manually
          </p>
        </div>
      );
    case 'disable_forwarding_rule':
      return (
        <div className="grid gap-3 sm:grid-cols-2">
          <div className="space-y-1.5">
            <Label htmlFor={`${actionType}-userPrincipalName`}>User principal name</Label>
            <Input
              id={`${actionType}-userPrincipalName`}
              value={str('userPrincipalName')}
              onChange={(e) => setField('userPrincipalName', e.target.value)}
            />
          </div>
          <div className="space-y-1.5">
            <Label htmlFor={`${actionType}-ruleName`}>Rule name</Label>
            <Input
              id={`${actionType}-ruleName`}
              value={str('ruleName')}
              onChange={(e) => setField('ruleName', e.target.value)}
            />
          </div>
        </div>
      );
    default:
      return null;
  }
}

/**
 * A button that is ALWAYS rendered (D-05 — never removed from the DOM), and
 * wrapped in a Tooltip explaining why it's disabled whenever `reason` is
 * non-null. Disabled native <button>s don't reliably fire hover events, so
 * the Tooltip trigger wraps a focusable <span> around the button rather than
 * the button itself.
 */
function GatedButton({
  children,
  disabled,
  reason,
  loading,
  onClick,
  variant,
}: {
  children: ReactNode;
  disabled: boolean;
  reason: string | null;
  loading?: boolean;
  onClick: () => void;
  variant?: 'default' | 'outline' | 'destructive';
}) {
  const button = (
    <Button variant={variant} disabled={disabled || !!loading} onClick={onClick}>
      {loading ? <Loader2 className="h-3.5 w-3.5 mr-1.5 animate-spin" /> : null}
      {children}
    </Button>
  );

  if (!disabled || !reason) return button;

  return (
    <Tooltip>
      <TooltipTrigger asChild>
        <span tabIndex={0} className="inline-block">
          {button}
        </span>
      </TooltipTrigger>
      <TooltipContent>{reason}</TooltipContent>
    </Tooltip>
  );
}

export function ActionAreaCard({
  campaignId,
  classification,
  remediationActions,
  campaignStatus,
  campaignUpdatedAt,
  evidence,
  onActionComplete,
}: ActionAreaCardProps) {
  const { data: session } = useSession();
  const role = (session?.user as { role?: string } | undefined)?.role ?? 'user';
  const canApprove = hasPermission(role, 'phishing', 'approve');
  const canRemediate = hasPermission(role, 'phishing', 'remediate');

  const [rows, setRows] = useState<Record<string, ActionRowState>>({});
  const [isApproving, setIsApproving] = useState(false);
  const [isRemediating, setIsRemediating] = useState(false);
  const [isMarkingFalsePositive, setIsMarkingFalsePositive] = useState(false);
  const [isMarkingAccidentalReport, setIsMarkingAccidentalReport] = useState(false);
  const [remediateDialogOpen, setRemediateDialogOpen] = useState(false);
  const [falsePositiveDialogOpen, setFalsePositiveDialogOpen] = useState(false);
  const [falsePositiveReason, setFalsePositiveReason] = useState('');
  const [accidentalReportDialogOpen, setAccidentalReportDialogOpen] = useState(false);
  const [accidentalReportReason, setAccidentalReportReason] = useState('');

  const recommendedActionsKey = classification?.recommendedActions.join(',') ?? '';

  useEffect(() => {
    if (!classification) {
      setRows({});
      return;
    }
    const next: Record<string, ActionRowState> = {};
    for (const actionType of classification.recommendedActions) {
      const params = deriveDefaultParams(actionType, evidence);
      if (actionType === 'purge_message' && Array.isArray(params.mailboxes)) {
        params.mailboxes = (params.mailboxes as string[]).join(', ');
      }
      next[actionType] = { checked: false, params };
    }
    setRows(next);
    // Re-derive only when the recommended-action list itself or the source
    // evidence changes — not on every render.
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [
    recommendedActionsKey,
    evidence.requesterEmail,
    evidence.senderEmail,
    evidence.senderDomain,
    evidence.messageId,
  ]);

  function updateRow(actionType: string, patch: Partial<ActionRowState>) {
    setRows((prev) => ({
      ...prev,
      [actionType]: { ...prev[actionType], ...patch },
    }));
  }

  // NULL-GUARD FIRST: classification is Classification | null — the default
  // state for a freshly-grouped campaign, since classification is never
  // auto-triggered. Never dereference classification.recommendedActions
  // before this guard.
  if (classification == null || classification.recommendedActions.length === 0) {
    return (
      <Card>
        <CardHeader>
          <CardTitle className="font-bold">
            <ShieldAlert className="h-4 w-4 mr-2 inline" />
            Action Area
          </CardTitle>
        </CardHeader>
        <CardContent>
          <p className="text-sm text-muted-foreground">
            No classification yet — classify this campaign to see recommended remediation actions.
          </p>
        </CardContent>
      </Card>
    );
  }

  // TS can't retain the null-guard's narrowing across the closures below
  // (handleApprove captures `classification` from the outer scope) — bind a
  // locally-narrowed const instead of asserting `!` at each use site.
  const activeClassification = classification;
  const checkedCount = Object.values(rows).filter((row) => row.checked).length;

  async function handleApprove() {
    const actions = activeClassification.recommendedActions
      .filter((actionType) => rows[actionType]?.checked)
      .map((actionType) => ({
        actionType,
        params: normalizeParamsForSubmit(actionType, rows[actionType].params),
      }));

    setIsApproving(true);
    try {
      const res = await fetch(`/api/phishing/campaigns/${campaignId}/approve`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ actions }),
      });
      const data = await res.json();
      if (!res.ok) throw new Error(data.message ?? data.error ?? 'Approve failed');
      toast.success(`Approved ${Array.isArray(data) ? data.length : actions.length} remediation action(s)`);
      onActionComplete();
    } catch (err) {
      toast.error(`Approve failed: ${err instanceof Error ? err.message : 'Unknown error'}`);
    } finally {
      setIsApproving(false);
    }
  }

  // D-05 "resolved" definition — a false-positive campaign or any completed
  // remediation. Once resolved, all three buttons stay in the DOM but are
  // disabled with a resolved-state tooltip.
  const completedAction = remediationActions.find((a) => a.status === 'completed');
  const resolved =
    campaignStatus === 'false_positive' || campaignStatus === 'accidental_report' || completedAction != null;

  function resolvedTooltipCopy(): string {
    if (completedAction) {
      const dateStr = completedAction.completedAt
        ? new Date(completedAction.completedAt).toLocaleDateString()
        : 'an earlier date';
      return `Already remediated on ${dateStr} by ${completedAction.approvedBy ?? 'unknown'}`;
    }
    if (campaignStatus === 'accidental_report') {
      return `Marked as an accidental report on ${new Date(campaignUpdatedAt).toLocaleDateString()}`;
    }
    return `Marked as false positive on ${new Date(campaignUpdatedAt).toLocaleDateString()}`;
  }

  const approvedActions = remediationActions.filter((a) => a.status === 'approved');
  const hasBlockingRemediation = remediationActions.some(
    (a) => a.status === 'approved' || a.status === 'completed'
  );

  // Priority-ordered disable reasons — first match wins (UI-SPEC Action Area
  // Spec, "Buttons" section). All permission checks use hasPermission() from
  // lib/permissions.ts — the exact function the server routes enforce —
  // never a bespoke role string check, so client and server can never drift
  // (REVIEW-06).
  const approveDisabledReason = !canApprove
    ? 'Requires approve permission'
    : resolved
      ? resolvedTooltipCopy()
      : checkedCount === 0
        ? 'Select at least one action to approve'
        : null;

  const remediateDisabledReason = !canRemediate
    ? 'Requires remediate permission'
    : resolved
      ? resolvedTooltipCopy()
      : approvedActions.length === 0
        ? 'No approved actions to remediate'
        : null;

  const markFalsePositiveDisabledReason = !canApprove
    ? 'Requires approve permission'
    : resolved
      ? resolvedTooltipCopy()
      : hasBlockingRemediation
        ? 'Cannot mark false positive — this campaign already has approved or completed remediation'
        : null;

  const markAccidentalReportDisabledReason = !canApprove
    ? 'Requires approve permission'
    : resolved
      ? resolvedTooltipCopy()
      : hasBlockingRemediation
        ? 'Cannot mark as accidental report — this campaign already has approved or completed remediation'
        : null;

  async function handleRemediateConfirm() {
    setIsRemediating(true);
    try {
      const res = await fetch(`/api/phishing/campaigns/${campaignId}/remediate`, { method: 'POST' });
      const data = await res.json();
      if (!res.ok) throw new Error(data.message ?? data.error ?? 'Remediate failed');
      const count = Array.isArray(data.actions) ? data.actions.length : approvedActions.length;
      toast.success(`Remediation completed for ${count} action(s)`);
      setRemediateDialogOpen(false);
      onActionComplete();
    } catch (err) {
      toast.error(`Remediate failed: ${err instanceof Error ? err.message : 'Unknown error'}`);
    } finally {
      setIsRemediating(false);
    }
  }

  async function handleMarkFalsePositiveConfirm() {
    setIsMarkingFalsePositive(true);
    try {
      const res = await fetch(`/api/phishing/campaigns/${campaignId}/mark-false-positive`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(falsePositiveReason ? { reason: falsePositiveReason } : {}),
      });
      const data = await res.json();
      if (!res.ok) throw new Error(data.message ?? data.error ?? 'Mark as false positive failed');
      toast.success('Campaign marked as false positive');
      setFalsePositiveDialogOpen(false);
      setFalsePositiveReason('');
      onActionComplete();
    } catch (err) {
      toast.error(`Mark as false positive failed: ${err instanceof Error ? err.message : 'Unknown error'}`);
    } finally {
      setIsMarkingFalsePositive(false);
    }
  }

  async function handleMarkAccidentalReportConfirm() {
    setIsMarkingAccidentalReport(true);
    try {
      const res = await fetch(`/api/phishing/campaigns/${campaignId}/mark-accidental-report`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(accidentalReportReason ? { reason: accidentalReportReason } : {}),
      });
      const data = await res.json();
      if (!res.ok) throw new Error(data.message ?? data.error ?? 'Mark as accidental report failed');
      if (data.notePosted === false) {
        toast.warning(
          `Campaign marked as accidental report, but the reporter note failed to post${
            data.noteError ? `: ${data.noteError}` : ''
          } — follow up manually.`
        );
      } else {
        toast.success('Marked as accidental report, reporter notified');
      }
      setAccidentalReportDialogOpen(false);
      setAccidentalReportReason('');
      onActionComplete();
    } catch (err) {
      toast.error(`Mark as accidental report failed: ${err instanceof Error ? err.message : 'Unknown error'}`);
    } finally {
      setIsMarkingAccidentalReport(false);
    }
  }

  return (
    <Card>
      <CardHeader>
        <CardTitle className="font-bold">
          <ShieldAlert className="h-4 w-4 mr-2 inline" />
          Action Area
        </CardTitle>
      </CardHeader>
      <CardContent className="space-y-4">
        <div className="space-y-3">
          {classification.recommendedActions.map((actionType) => {
            const row = rows[actionType];
            if (!row) return null;
            return (
              <div key={actionType} className="rounded-md border p-3 space-y-3">
                <div className="flex items-center gap-2">
                  <Checkbox
                    id={`action-${actionType}`}
                    checked={row.checked}
                    onCheckedChange={(checked) => updateRow(actionType, { checked: checked === true })}
                  />
                  <Label htmlFor={`action-${actionType}`}>{humanizeAction(actionType)}</Label>
                </div>
                <ActionParamsForm
                  actionType={actionType}
                  params={row.params}
                  onChange={(params) => updateRow(actionType, { params })}
                />
              </div>
            );
          })}
        </div>

        <TooltipProvider>
          <div className="flex flex-wrap gap-2">
            <GatedButton
              disabled={!!approveDisabledReason}
              reason={approveDisabledReason}
              loading={isApproving}
              onClick={handleApprove}
            >
              Approve selected
            </GatedButton>

            <GatedButton
              disabled={!!remediateDisabledReason}
              reason={remediateDisabledReason}
              loading={isRemediating}
              variant="outline"
              onClick={() => setRemediateDialogOpen(true)}
            >
              Remediate approved actions
            </GatedButton>

            <GatedButton
              disabled={!!markFalsePositiveDisabledReason}
              reason={markFalsePositiveDisabledReason}
              loading={isMarkingFalsePositive}
              variant="destructive"
              onClick={() => setFalsePositiveDialogOpen(true)}
            >
              Mark as false positive
            </GatedButton>

            <GatedButton
              disabled={!!markAccidentalReportDisabledReason}
              reason={markAccidentalReportDisabledReason}
              loading={isMarkingAccidentalReport}
              variant="outline"
              onClick={() => setAccidentalReportDialogOpen(true)}
            >
              Mark as accidental report
            </GatedButton>
          </div>
        </TooltipProvider>

        <AlertDialog open={remediateDialogOpen} onOpenChange={setRemediateDialogOpen}>
          <AlertDialogContent>
            <AlertDialogHeader>
              <AlertDialogTitle>Remediate approved actions?</AlertDialogTitle>
              <AlertDialogDescription>
                Remediate {approvedActions.length} approved action(s):{' '}
                {approvedActions.map((a) => humanizeAction(a.actionType)).join(', ')}. This executes the
                simulated remediation effect and cannot be undone.
              </AlertDialogDescription>
            </AlertDialogHeader>
            <AlertDialogFooter>
              <AlertDialogCancel disabled={isRemediating}>Cancel</AlertDialogCancel>
              <AlertDialogAction
                className={buttonVariants({ variant: 'destructive' })}
                disabled={isRemediating}
                onClick={(e) => {
                  e.preventDefault();
                  void handleRemediateConfirm();
                }}
              >
                Remediate
              </AlertDialogAction>
            </AlertDialogFooter>
          </AlertDialogContent>
        </AlertDialog>

        <AlertDialog open={falsePositiveDialogOpen} onOpenChange={setFalsePositiveDialogOpen}>
          <AlertDialogContent>
            <AlertDialogHeader>
              <AlertDialogTitle>Mark as false positive?</AlertDialogTitle>
              <AlertDialogDescription>
                Mark this campaign as a false positive? This cannot be undone — there is no way to reverse it
                later.
              </AlertDialogDescription>
            </AlertDialogHeader>
            <div className="space-y-1.5">
              <Label htmlFor="false-positive-reason">Reason (optional)</Label>
              <Textarea
                id="false-positive-reason"
                value={falsePositiveReason}
                onChange={(e) => setFalsePositiveReason(e.target.value)}
              />
            </div>
            <AlertDialogFooter>
              <AlertDialogCancel disabled={isMarkingFalsePositive}>Cancel</AlertDialogCancel>
              <AlertDialogAction
                className={buttonVariants({ variant: 'destructive' })}
                disabled={isMarkingFalsePositive}
                onClick={(e) => {
                  e.preventDefault();
                  void handleMarkFalsePositiveConfirm();
                }}
              >
                Mark as false positive
              </AlertDialogAction>
            </AlertDialogFooter>
          </AlertDialogContent>
        </AlertDialog>

        <AlertDialog open={accidentalReportDialogOpen} onOpenChange={setAccidentalReportDialogOpen}>
          <AlertDialogContent>
            <AlertDialogHeader>
              <AlertDialogTitle>Mark as an accidental report?</AlertDialogTitle>
              <AlertDialogDescription>
                Mark this campaign as an accidental report? This posts a note to the reporting employee
                explaining no action is needed, and closes out the campaign. This cannot be undone.
              </AlertDialogDescription>
            </AlertDialogHeader>
            <div className="space-y-1.5">
              <Label htmlFor="accidental-report-reason">Reason (optional)</Label>
              <Textarea
                id="accidental-report-reason"
                value={accidentalReportReason}
                onChange={(e) => setAccidentalReportReason(e.target.value)}
              />
            </div>
            <AlertDialogFooter>
              <AlertDialogCancel disabled={isMarkingAccidentalReport}>Cancel</AlertDialogCancel>
              <AlertDialogAction
                disabled={isMarkingAccidentalReport}
                onClick={(e) => {
                  e.preventDefault();
                  void handleMarkAccidentalReportConfirm();
                }}
              >
                Mark as accidental report
              </AlertDialogAction>
            </AlertDialogFooter>
          </AlertDialogContent>
        </AlertDialog>
      </CardContent>
    </Card>
  );
}
