"use client";

import { useState } from "react";

import {
  previewAlertCondition,
  seedAlertRule,
} from "@/app/(prowler)/alerts/_actions";
import { listAlertRecipients } from "@/app/(prowler)/alerts/_actions/recipients";
import {
  ALERT_TRIGGER_KINDS,
  type AlertCondition,
  type AlertPreviewResponse,
  type AlertRecipient,
  type AlertRule,
  type AlertTriggerKind,
} from "@/app/(prowler)/alerts/_types";
import type { FilterChip } from "@/components/filters/filter-summary-strip";
import { FilterSummaryStrip } from "@/components/filters/filter-summary-strip";
import { FindingsFilterBatchControls } from "@/components/findings/findings-filters";
import {
  Badge,
  Button,
  Card,
  CardContent,
  Field,
  FieldError,
  FieldLabel,
  Input,
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
  Skeleton,
  Textarea,
} from "@/components/shadcn";
import { Modal } from "@/components/shadcn/modal";
import {
  MultiSelect,
  MultiSelectContent,
  MultiSelectItem,
  MultiSelectSelectAll,
  MultiSelectSeparator,
  MultiSelectTrigger,
  MultiSelectValue,
} from "@/components/shadcn/select/multiselect";
import { useMountEffect } from "@/hooks/use-mount-effect";
import type { ScanEntity } from "@/types";
import type { ProviderProps } from "@/types/providers";

import {
  getAlertFormDefaults,
  getEmptyAlertFormDefaults,
  getFindingsFiltersFromAlertCondition,
} from "../_lib/alert-adapter";
import { getAlertMutationError } from "../_lib/alert-errors";
import { alertFormSchema } from "../_lib/alert-form-schema";
import type {
  AlertFormSubmitResult,
  AlertFormValues,
} from "../_types/alert-form";
import { ALERT_NOTIFICATION_METHODS } from "../_types/alert-form";

interface AlertFormModalProps {
  open: boolean;
  defaultFrequency: AlertTriggerKind;
  providers?: ProviderProps[];
  completedScanIds?: string[];
  scanDetails?: { [key: string]: ScanEntity }[];
  uniqueRegions?: string[];
  uniqueServices?: string[];
  uniqueResourceTypes?: string[];
  uniqueCategories?: string[];
  uniqueGroups?: string[];
  editingAlert?: AlertRule | null;
  seededCondition?: AlertCondition | null;
  selectedFindingsFilterChips?: FilterChip[];
  defaultName?: string;
  onOpenChange: (open: boolean) => void;
  onSubmit: (values: AlertFormValues) => Promise<AlertFormSubmitResult>;
}

interface FormErrors {
  name?: string;
  recipientEmails?: string;
  root?: string;
}

const normalizeEmail = (email: string): string => email.trim().toLowerCase();

const getRecipientEmails = (selectedEmails: Set<string>): string[] =>
  Array.from(selectedEmails);

const ALERT_FREQUENCY_OPTIONS = [
  {
    value: ALERT_TRIGGER_KINDS.AFTER_SCAN,
    label: "After each scan",
  },
  {
    value: ALERT_TRIGGER_KINDS.DAILY,
    label: "Daily digest",
  },
  {
    value: ALERT_TRIGGER_KINDS.BOTH,
    label: "After each scan and daily",
  },
] as const;

const ALERT_SEED_ERROR = "Apply at least one alert-compatible Findings filter.";

const serializeCondition = (condition: AlertCondition | null): string =>
  condition ? JSON.stringify(condition) : "none";

const getAlertFormModalResetKey = ({
  open,
  defaultFrequency,
  editingAlert,
  seededCondition,
}: Pick<
  AlertFormModalProps,
  "open" | "defaultFrequency" | "editingAlert" | "seededCondition"
>): string =>
  [
    open ? "open" : "closed",
    editingAlert?.id ?? "create",
    editingAlert?.attributes.updated_at ?? "",
    defaultFrequency,
    serializeCondition(seededCondition ?? null),
  ].join("|");

const allowInitialDialogFocus = () => undefined;

const uniqueValues = (values: string[]): string[] =>
  Array.from(new Set(values));

interface PreviewState {
  status: "success" | "error";
  data?: AlertPreviewResponse;
  error?: string;
}

const formatPreviewNumber = (value: number): string =>
  new Intl.NumberFormat("en-US").format(value);

const getPreviewSeverityLabel = (severity: string): string =>
  severity.charAt(0).toUpperCase() + severity.slice(1);

const getPreviewMessage = (data: AlertPreviewResponse): string => {
  const totalFindings = data.summary.finding_count_total ?? 0;
  if (totalFindings === 0) {
    return "These filters did not match any findings for the latest scan.";
  }

  const findingLabel = totalFindings === 1 ? "finding" : "findings";
  const topSeverity = data.summary.top_severity;
  const severityClause = topSeverity
    ? `, including ${getPreviewSeverityLabel(topSeverity)} severity`
    : "";

  return `It found ${formatPreviewNumber(totalFindings)} ${findingLabel}${severityClause}.`;
};

const PreviewSummarySkeleton = () => (
  <Card variant="inner" padding="sm">
    <CardContent className="flex flex-col gap-2">
      <div className="flex items-center justify-between gap-2">
        <Skeleton className="h-5 w-28" />
        <Skeleton className="h-5 w-20 rounded-full" />
      </div>
      <Skeleton className="h-4 w-full" />
    </CardContent>
  </Card>
);

const PreviewSummary = ({ preview }: { preview: PreviewState }) => {
  if (preview.status === "error") {
    return (
      <Card variant="danger" padding="sm">
        <CardContent className="flex flex-col gap-2">
          <div className="flex items-center justify-between gap-2">
            <span className="text-text-neutral-primary text-sm font-medium">
              Test result
            </span>
            <Badge variant="tag">Error</Badge>
          </div>
          <p className="text-text-error-primary text-sm">{preview.error}</p>
        </CardContent>
      </Card>
    );
  }

  const data = preview.data;
  if (!data) return null;

  return (
    <Card variant="inner" padding="sm">
      <CardContent className="flex flex-col gap-2">
        <span className="text-text-neutral-primary text-sm font-medium">
          Test result
        </span>
        <p className="text-text-neutral-secondary text-sm">
          {getPreviewMessage(data)}
        </p>
      </CardContent>
    </Card>
  );
};

const normalizeFindingsFilterKey = (filterKey: string): string =>
  filterKey.startsWith("filter[") ? filterKey : `filter[${filterKey}]`;

interface AlertRecipientsSelectProps {
  selectedEmails: Set<string>;
  onValuesChange: (emails: string[]) => void;
}

interface RecipientOption {
  email: string;
  status?: AlertRecipient["attributes"]["status"];
}

const getRecipientStatusLabel = (
  status: AlertRecipient["attributes"]["status"],
): string => status.charAt(0).toUpperCase() + status.slice(1);

const getRecipientOptions = (
  recipients: AlertRecipient[],
  selectedEmails: string[],
): RecipientOption[] => {
  const options = new Map<string, RecipientOption>();

  recipients.forEach((recipient) => {
    const email = normalizeEmail(recipient.attributes.email);
    if (!email) return;
    options.set(email, { email, status: recipient.attributes.status });
  });

  selectedEmails.forEach((email) => {
    const normalizedEmail = normalizeEmail(email);
    if (!normalizedEmail || options.has(normalizedEmail)) return;
    options.set(normalizedEmail, { email: normalizedEmail });
  });

  return Array.from(options.values()).sort((left, right) =>
    left.email.localeCompare(right.email),
  );
};

const AlertRecipientsSelect = ({
  selectedEmails,
  onValuesChange,
}: AlertRecipientsSelectProps) => {
  const [recipients, setRecipients] = useState<AlertRecipient[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  useMountEffect(() => {
    listAlertRecipients({
      "page[size]": "100",
      sort: "email",
    }).then((result) => {
      setLoading(false);
      if (result?.error) {
        setRecipients([]);
        setError(result.error);
        return;
      }
      setRecipients(result.data);
      setError(null);
    });
  });

  const selectedValues = Array.from(selectedEmails);
  const options = getRecipientOptions(recipients, selectedValues);

  return (
    <div className="flex flex-col gap-2">
      <MultiSelect values={selectedValues} onValuesChange={onValuesChange}>
        <MultiSelectTrigger
          id="alert-recipients"
          aria-label="Recipients"
          size="default"
        >
          <MultiSelectValue
            placeholder={loading ? "Loading recipients" : "Select emails"}
          />
        </MultiSelectTrigger>
        <MultiSelectContent
          search={{
            placeholder: "Search recipients...",
            emptyMessage: "No confirmed recipients found.",
          }}
          width="wide"
        >
          <MultiSelectSelectAll
            mode="select"
            values={options.map((option) => option.email)}
          >
            Select All
          </MultiSelectSelectAll>
          <MultiSelectSeparator />
          {options.map((option) => (
            <MultiSelectItem
              key={option.email}
              value={option.email}
              badgeLabel={option.email}
              keywords={[option.email, option.status ?? ""]}
            >
              <span className="truncate">{option.email}</span>
              {option.status && (
                <Badge variant="tag">
                  {getRecipientStatusLabel(option.status)}
                </Badge>
              )}
            </MultiSelectItem>
          ))}
        </MultiSelectContent>
      </MultiSelect>
      {error && <p className="text-text-error-primary text-xs">{error}</p>}
    </div>
  );
};

export const AlertFormModal = (props: AlertFormModalProps) => {
  const resetKey = getAlertFormModalResetKey(props);

  return <AlertFormModalContent key={resetKey} {...props} />;
};

const AlertFormModalContent = ({
  open,
  defaultFrequency,
  providers = [],
  completedScanIds = [],
  scanDetails = [],
  uniqueRegions = [],
  uniqueServices = [],
  uniqueResourceTypes = [],
  uniqueCategories = [],
  uniqueGroups = [],
  editingAlert = null,
  seededCondition = null,
  selectedFindingsFilterChips = [],
  defaultName = "Findings filter alert",
  onOpenChange,
  onSubmit,
}: AlertFormModalProps) => {
  const defaults = editingAlert
    ? getAlertFormDefaults(editingAlert)
    : getEmptyAlertFormDefaults(defaultFrequency, seededCondition ?? undefined);
  const initialName = editingAlert
    ? defaults.name
    : defaults.name || defaultName;

  // Local state needed: user edits are buffered until the modal form is submitted.
  const [name, setName] = useState(initialName);
  const [description, setDescription] = useState(defaults.description);
  const [frequency, setFrequency] = useState<AlertTriggerKind>(
    defaults.frequency,
  );
  const [pendingFilters, setPendingFilters] = useState<
    Record<string, string[]>
  >(
    editingAlert
      ? getFindingsFiltersFromAlertCondition(editingAlert.attributes.condition)
      : {},
  );
  const [selectedRecipientEmails, setSelectedRecipientEmails] = useState(
    () => new Set(defaults.recipientEmails.map(normalizeEmail)),
  );
  const [errors, setErrors] = useState<FormErrors>({});
  const [saving, setSaving] = useState(false);
  const [previewLoading, setPreviewLoading] = useState(false);
  const [preview, setPreview] = useState<PreviewState | null>(null);

  const submitLabel = editingAlert ? "Save" : "Create";

  const setRecipientEmails = (emails: string[]) =>
    setSelectedRecipientEmails(
      new Set(emails.map(normalizeEmail).filter(Boolean)),
    );

  const setPendingFilter = (filterKey: string, values: string[]) => {
    setPendingFilters((current) => ({
      ...current,
      [normalizeFindingsFilterKey(filterKey)]: uniqueValues(values),
    }));
    setPreview(null);
  };

  const getPendingFilterValue = (filterKey: string): string[] =>
    pendingFilters[normalizeFindingsFilterKey(filterKey)] ?? [];

  const buildCurrentValues = (condition: AlertCondition): AlertFormValues => ({
    name,
    description,
    method: ALERT_NOTIFICATION_METHODS.EMAIL,
    frequency,
    condition,
    recipientEmails: getRecipientEmails(selectedRecipientEmails),
    enabled: defaults.enabled,
  });

  const handlePreview = async () => {
    if (!editingAlert) return;

    const seedResult = await seedAlertRule(pendingFilters);
    if (seedResult?.error) {
      setPreview({
        status: "error",
        error: getAlertMutationError(seedResult, ALERT_SEED_ERROR),
      });
      return;
    }

    const values = buildCurrentValues(seedResult.data.attributes.condition);
    const parsed = alertFormSchema.safeParse(values);
    if (!parsed.success) {
      setPreview({
        status: "error",
        error: "Fix alert fields before running test.",
      });
      return;
    }

    setPreviewLoading(true);
    const result = await previewAlertCondition({
      condition: parsed.data.condition,
    });
    setPreviewLoading(false);

    if (result?.error) {
      setPreview({ status: "error", error: result.error });
      return;
    }

    const previewData = result.data.attributes as AlertPreviewResponse;
    if (previewData.evaluation_failed) {
      setPreview({
        status: "error",
        error: previewData.last_error ?? "Preview evaluation failed.",
      });
      return;
    }

    setPreview({ status: "success", data: previewData });
  };

  const handleSubmit = async () => {
    const seedResult = editingAlert
      ? await seedAlertRule(pendingFilters)
      : null;
    if (seedResult?.error) {
      setPreview(null);
      setErrors({
        root: getAlertMutationError(seedResult, ALERT_SEED_ERROR),
      });
      return;
    }

    const values = buildCurrentValues(
      seedResult?.data.attributes.condition ?? defaults.condition,
    );
    const parsed = alertFormSchema.safeParse(values);
    if (!parsed.success) {
      const fieldErrors = parsed.error.flatten().fieldErrors;
      setErrors({
        name: fieldErrors.name?.[0],
        recipientEmails: fieldErrors.recipientEmails?.[0],
      });
      return;
    }

    setSaving(true);
    const result = await onSubmit(parsed.data);
    setSaving(false);
    if (result.ok) {
      setErrors({});
      onOpenChange(false);
      return;
    }
    setErrors({ root: result.error ?? "Could not save alert." });
  };

  return (
    <Modal
      open={open}
      onOpenChange={onOpenChange}
      title={editingAlert ? "Edit Alert" : "Create Alert"}
      description={
        editingAlert
          ? "Update recipients, frequency, and finding filters for this alert."
          : "Create an alert from the current Findings filters."
      }
      onOpenAutoFocus={allowInitialDialogFocus}
      size={editingAlert ? "5xl" : "xl"}
      className={
        editingAlert
          ? "minimal-scrollbar max-h-[calc(100vh-2rem)] overflow-y-auto"
          : undefined
      }
    >
      <div className="flex flex-col gap-4">
        <FilterSummaryStrip chips={selectedFindingsFilterChips} />
        <Field>
          <FieldLabel htmlFor="alert-name">Name</FieldLabel>
          <Input
            id="alert-name"
            aria-label="Name"
            value={name}
            onChange={(event) => setName(event.target.value)}
          />
          {errors.name && <FieldError>{errors.name}</FieldError>}
        </Field>
        <Field>
          <FieldLabel htmlFor="alert-description">Description</FieldLabel>
          <Textarea
            id="alert-description"
            aria-label="Description"
            textareaSize="lg"
            value={description}
            onChange={(event) => setDescription(event.target.value)}
          />
        </Field>
        <Field>
          <FieldLabel htmlFor="alert-frequency">Frequency</FieldLabel>
          <Select
            value={frequency}
            onValueChange={(value) => {
              setFrequency(value as AlertTriggerKind);
              setPreview(null);
            }}
          >
            <SelectTrigger id="alert-frequency" aria-label="Frequency">
              <SelectValue placeholder="Select frequency" />
            </SelectTrigger>
            <SelectContent width="wide" className="z-[60]">
              {ALERT_FREQUENCY_OPTIONS.map((option) => (
                <SelectItem key={option.value} value={option.value}>
                  {option.label}
                </SelectItem>
              ))}
            </SelectContent>
          </Select>
        </Field>
        <Field>
          <FieldLabel htmlFor="alert-recipients">Recipients</FieldLabel>
          <AlertRecipientsSelect
            selectedEmails={selectedRecipientEmails}
            onValuesChange={setRecipientEmails}
          />
          {errors.recipientEmails && (
            <FieldError>{errors.recipientEmails}</FieldError>
          )}
        </Field>
        {editingAlert && (
          <div className="flex flex-col gap-3">
            <Card variant="inner" padding="sm">
              <CardContent className="flex flex-col gap-3">
                <h3 className="text-text-neutral-primary text-sm font-medium">
                  Filters
                </h3>
                <FindingsFilterBatchControls
                  providers={providers}
                  completedScanIds={completedScanIds}
                  scanDetails={scanDetails}
                  uniqueRegions={uniqueRegions}
                  uniqueServices={uniqueServices}
                  uniqueResourceTypes={uniqueResourceTypes}
                  uniqueCategories={uniqueCategories}
                  uniqueGroups={uniqueGroups}
                  appliedFilters={{}}
                  pendingFilters={pendingFilters}
                  changedFilters={pendingFilters}
                  setPending={setPendingFilter}
                  getFilterValue={getPendingFilterValue}
                  showSummaries={false}
                  variant="alerts-edit"
                />
              </CardContent>
            </Card>
            {(previewLoading || preview) && (
              <div className="pt-1">
                {previewLoading ? (
                  <PreviewSummarySkeleton />
                ) : (
                  preview && <PreviewSummary preview={preview} />
                )}
              </div>
            )}
          </div>
        )}
        {errors.root && (
          <div className="text-text-error-primary text-sm">{errors.root}</div>
        )}
        {/* mt-4 lifts the gap-4 container spacing to 32px so the distance to
            the footer matches the launch scan and triage note modals. */}
        <div className="mt-4 flex w-full justify-between gap-4">
          <Button
            variant="outline"
            size="lg"
            onClick={() => onOpenChange(false)}
          >
            Cancel
          </Button>
          <div className="flex gap-4">
            {editingAlert && (
              <Button
                variant="outline"
                size="lg"
                onClick={handlePreview}
                disabled={previewLoading || saving}
              >
                {previewLoading ? "Running..." : "Test"}
              </Button>
            )}
            <Button size="lg" onClick={handleSubmit} disabled={saving}>
              {submitLabel}
            </Button>
          </div>
        </div>
      </div>
    </Modal>
  );
};
