"use client";

import {
  Alert,
  Button,
  DatePicker,
  Form,
  Input,
  Select,
  Space,
  Typography,
} from "antd";
import dayjs, { type Dayjs } from "dayjs";
import { useRouter } from "next/navigation";
import { useMemo, useRef, useState, type ReactNode } from "react";

import { CopyOnceModal } from "@/app/dashboard/api-keys/components/copy-once-modal";
import {
  apiKeysApi,
  type CreateAPIKeyResponse,
} from "@/lib/api/api-keys";
import { extractApiErrorMessage } from "@/lib/api/errors";
import { useAuth } from "@/lib/hooks/use-auth";

const { Title, Paragraph, Text } = Typography;

const AGENT_NAME_MAX_LENGTH = 50;
const DEFAULT_EXPIRES_DAYS = 90;

// Keep in sync with the read-only allowlist in backend middleware/agent_scope.go:
// prefixes caasm:view_ / vuln:view_, explicitly excluding vuln:view_sensitive_details.
const READ_ONLY_SCOPE_PREFIXES = ["caasm:view_", "vuln:view_"];
const READ_ONLY_SCOPE_DENYLIST = new Set(["vuln:view_sensitive_details"]);

// Keep in sync with the write-scope allowlist opened to agents in backend
// middleware/agent_scope.go (iteration-002: vuln write operations for Agents;
// iteration-003 added vuln:submit_findings).
const WRITE_SCOPES = [
  "vuln:edit_details",
  "vuln:request_retest",
  "vuln:close_findings",
  "vuln:submit_findings",
];
const WRITE_SCOPE_SET = new Set(WRITE_SCOPES);
// Decision 3 (plan §2): vuln:view_findings is a prerequisite of every write scope.
const VIEW_FINDINGS_SCOPE = "vuln:view_findings";

type FormValues = {
  agent_name: string;
  permission_scope: string[];
  expires_at: Dayjs;
};

type ScopeOption = { label: string; value: string };

function isReadOnlyScope(permission: string) {
  if (READ_ONLY_SCOPE_DENYLIST.has(permission)) return false;
  return READ_ONLY_SCOPE_PREFIXES.some((prefix) =>
    permission.startsWith(prefix),
  );
}

function isWriteScope(permission: string) {
  return WRITE_SCOPE_SET.has(permission);
}

export default function NewAPIKeyPage() {
  const router = useRouter();
  const { user } = useAuth();
  const [form] = Form.useForm<FormValues>();
  const [submitting, setSubmitting] = useState(false);
  const [submitError, setSubmitError] = useState<string | null>(null);
  const [createdKey, setCreatedKey] = useState<CreateAPIKeyResponse | null>(
    null,
  );
  const defaultExpiresAt = useMemo(
    () => dayjs().add(DEFAULT_EXPIRES_DAYS, "day"),
    [],
  );

  const selectedScopes = Form.useWatch("permission_scope", form) ?? [];
  const hasWriteScopeSelected = selectedScopes.some(isWriteScope);
  // Snapshot of the previous selection, used in handleValuesChange to tell
  // "a write scope was just added" apart from "vuln:view_findings was removed".
  const prevScopesRef = useRef<string[]>([]);

  // scope options = user's own permissions ∩ (read-only allowlist ∪ write scopes),
  // grouped read-only vs write; permissions the user does not hold are not shown.
  // The backend subset check validates exactly claims.Permissions, so filter
  // user.permissions directly here instead of going through hasPermission's
  // admin bypass. Write scopes are only offered when the user also holds
  // vuln:view_findings — without it the backend would reject the key anyway.
  const { scopeGroups, scopeCount } = useMemo(() => {
    const permissions = user?.permissions ?? [];
    const toOption = (permission: string) => ({ label: permission, value: permission });
    const readOnlyOptions = permissions.filter(isReadOnlyScope).sort().map(toOption);
    const writeOptions = permissions.includes(VIEW_FINDINGS_SCOPE)
      ? permissions.filter(isWriteScope).sort().map(toOption)
      : [];
    const groups: { label: ReactNode; options: ScopeOption[] }[] = [];
    if (readOnlyOptions.length > 0) {
      groups.push({ label: "Read-only", options: readOnlyOptions });
    }
    if (writeOptions.length > 0) {
      groups.push({
        label: <Text type="warning">Write — grant to trusted Agents only</Text>,
        options: writeOptions,
      });
    }
    return {
      scopeGroups: groups,
      scopeCount: readOnlyOptions.length + writeOptions.length,
    };
  }, [user?.permissions]);

  // Decision 3 (plan §2): vuln:view_findings is the prerequisite of every write
  // scope. Enforce the linkage at the UX level; the backend validates it too.
  const handleValuesChange = (changed: Partial<FormValues>) => {
    if (!changed.permission_scope) return;
    const next = changed.permission_scope;
    const prev = prevScopesRef.current;
    let adjusted = next;
    if (
      prev.includes(VIEW_FINDINGS_SCOPE) &&
      !next.includes(VIEW_FINDINGS_SCOPE) &&
      next.some(isWriteScope)
    ) {
      // vuln:view_findings was removed while write scopes remain: drop them too.
      adjusted = next.filter((scope) => !isWriteScope(scope));
    } else if (next.some(isWriteScope) && !next.includes(VIEW_FINDINGS_SCOPE)) {
      // A write scope was just selected: force-include its prerequisite.
      adjusted = [...next, VIEW_FINDINGS_SCOPE];
    }
    prevScopesRef.current = adjusted;
    if (adjusted !== next) {
      form.setFieldValue("permission_scope", adjusted);
    }
  };

  const resetForm = () => {
    form.resetFields();
    prevScopesRef.current = [];
  };

  const handleFinish = async (values: FormValues) => {
    setSubmitting(true);
    setSubmitError(null);
    try {
      const created = await apiKeysApi.create({
        agent_name: values.agent_name.trim(),
        permission_scope: values.permission_scope,
        expires_at: values.expires_at.toDate().toISOString(),
      });
      resetForm();
      setCreatedKey(created);
    } catch (error) {
      // When the backend body code is non-zero, apiClient throws an Error that
      // already carries the backend message
      // (e.g. "scope exceeds your permissions or not read-only" / "expires_at is required").
      setSubmitError(extractApiErrorMessage(error) ?? "Failed to create API key. Please try again later.");
    } finally {
      setSubmitting(false);
    }
  };

  // Sole exit from the plaintext modal (after the "I have saved it" second
  // confirmation): clear the plaintext from memory and return to the list page.
  const handleKeyModalClose = () => {
    setCreatedKey(null);
    router.push("/dashboard/api-keys");
  };

  return (
    <Space direction="vertical" size={16} style={{ width: "100%" }}>
      <div>
        <Title level={3} style={{ marginBottom: 4 }}>
          Create API Key
        </Title>
        <Paragraph type="secondary" style={{ margin: 0 }}>
          Issue an access credential for an Agent. The permission scope you can
          grant is limited to the read-only and vulnerability write permissions
          your account already holds.
        </Paragraph>
      </div>

      {submitError ? (
        <Alert
          type="error"
          showIcon
          message="Failed to create API key"
          description={submitError}
        />
      ) : null}

      {scopeCount === 0 ? (
        <Alert
          type="warning"
          showIcon
          message="Your account has no permissions that can be granted to an Agent"
          description="An API Key can only carry permissions you already hold: read-only (caasm:view_* / vuln:view_*) or the vulnerability write scopes (vuln:edit_details / vuln:request_retest / vuln:close_findings / vuln:submit_findings). Contact an administrator to be granted permissions before creating one."
        />
      ) : null}

      {hasWriteScopeSelected ? (
        <Alert
          type="warning"
          showIcon
          message="This key will be able to modify vulnerability data"
          description="Write scopes let an Agent submit new findings, edit findings, request retests, or close findings — grant them only to trusted Agents. vuln:view_findings is included automatically as their prerequisite; removing it removes the write scopes too."
        />
      ) : null}

      <Form<FormValues>
        form={form}
        layout="vertical"
        style={{ maxWidth: 640 }}
        initialValues={{
          agent_name: "",
          permission_scope: [],
          expires_at: defaultExpiresAt,
        }}
        onFinish={handleFinish}
        onValuesChange={handleValuesChange}
      >
        <Form.Item
          name="agent_name"
          label="Agent name"
          rules={[
            { required: true, whitespace: true, message: "Please enter an Agent name" },
            {
              max: AGENT_NAME_MAX_LENGTH,
              message: `Agent name must be at most ${AGENT_NAME_MAX_LENGTH} characters`,
            },
          ]}
        >
          <Input
            placeholder="e.g. asset-inventory-bot"
            maxLength={AGENT_NAME_MAX_LENGTH}
            showCount
          />
        </Form.Item>

        <Form.Item
          name="permission_scope"
          label="Permission scope"
          extra="Only the permissions you currently hold are listed; the key's permissions never exceed your own. Selecting any write scope automatically includes vuln:view_findings."
          rules={[
            {
              required: true,
              type: "array",
              min: 1,
              message: "Select at least one permission",
            },
          ]}
        >
          <Select
            mode="multiple"
            placeholder="Select one or more permissions"
            options={scopeGroups}
            optionFilterProp="label"
          />
        </Form.Item>

        <Form.Item
          name="expires_at"
          label="Expiration time"
          extra={`Defaults to ${DEFAULT_EXPIRES_DAYS} days from now. There is no "never expires" option: production does not allow permanent keys, so an expiration time is required.`}
          rules={[
            { required: true, message: "Please select an expiration time" },
            {
              validator: (_, value: Dayjs | undefined) => {
                if (!value || value.isAfter(dayjs())) {
                  return Promise.resolve();
                }
                return Promise.reject(new Error("Expiration time must be later than the current time"));
              },
            },
          ]}
        >
          <DatePicker
            showTime
            style={{ width: "100%" }}
            disabledDate={(current) =>
              Boolean(current && current.isBefore(dayjs(), "day"))
            }
          />
        </Form.Item>

        <Form.Item>
          <Space>
            <Button
              type="primary"
              htmlType="submit"
              loading={submitting}
              disabled={scopeCount === 0}
            >
              Create API Key
            </Button>
            <Button htmlType="button" onClick={resetForm}>
              Reset
            </Button>
            <Button
              htmlType="button"
              onClick={() => router.push("/dashboard/api-keys")}
            >
              Back to list
            </Button>
          </Space>
        </Form.Item>
      </Form>

      <CopyOnceModal
        open={Boolean(createdKey)}
        apiKey={createdKey?.api_key ?? ""}
        onClose={handleKeyModalClose}
      />
    </Space>
  );
}
