import { useState, useEffect, useRef } from 'react';
import { useParams, useLocation, useNavigate } from 'react-router';
import { AlertDialog, Button, Tabs, useToast } from '@cameleer/design-system';
import { useEnvironmentStore } from '../../../api/environment-store';
import { useEnvironments } from '../../../api/queries/admin/environments';
import {
  useApps,
  useCreateApp,
  useDeleteApp,
  useAppVersions,
  useUploadJar,
  useDeployments,
  useCreateDeployment,
  useStopDeployment,
  useUpdateContainerConfig,
  useDirtyState,
} from '../../../api/queries/admin/apps';
import type { Deployment } from '../../../api/queries/admin/apps';
import { useApplicationConfig, useUpdateApplicationConfig } from '../../../api/queries/commands';
import { PageLoader } from '../../../components/PageLoader';
import { IdentitySection } from './IdentitySection';
import { Checkpoints } from './Checkpoints';
import { MonitoringTab } from './ConfigTabs/MonitoringTab';
import { ResourcesTab } from './ConfigTabs/ResourcesTab';
import { VariablesTab } from './ConfigTabs/VariablesTab';
import { SensitiveKeysTab } from './ConfigTabs/SensitiveKeysTab';
import { TracesTapsTab } from './ConfigTabs/TracesTapsTab';
import { RouteRecordingTab } from './ConfigTabs/RouteRecordingTab';
import { DeploymentTab } from './DeploymentTab/DeploymentTab';
import { PrimaryActionButton, computeMode } from './PrimaryActionButton';
import { useDeploymentPageState } from './hooks/useDeploymentPageState';
import { useFormDirty } from './hooks/useFormDirty';
import { useUnsavedChangesBlocker } from './hooks/useUnsavedChangesBlocker';
import { deriveAppName } from './utils/deriveAppName';
import styles from './AppDeploymentPage.module.css';

type TabKey = 'monitoring' | 'resources' | 'variables' | 'sensitive-keys' | 'deployment' | 'traces' | 'recording';

function slugify(name: string): string {
  return name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').substring(0, 100);
}

export default function AppDeploymentPage() {
  const { appId } = useParams<{ appId?: string }>();
  const location = useLocation();
  const navigate = useNavigate();
  const { toast } = useToast();
  const selectedEnv = useEnvironmentStore((s) => s.environment);
  const { data: environments = [], isLoading: envLoading } = useEnvironments();
  const { data: apps = [], isLoading: appsLoading } = useApps(selectedEnv);

  const isNetNew = location.pathname.endsWith('/apps/new');
  const app = isNetNew ? null : (apps.find((a) => a.slug === appId) ?? null);
  const env = environments.find((e) => e.slug === selectedEnv);

  const { data: versions = [] } = useAppVersions(selectedEnv, app?.slug);
  const currentVersion = versions.slice().sort((a, b) => b.version - a.version)[0] ?? null;
  const { data: deployments = [] } = useDeployments(selectedEnv, app?.slug);
  const currentDeployment = deployments.find((d) => d.status === 'RUNNING') ?? null;
  const activeDeployment = deployments.find((d) => d.status === 'STARTING') ?? null;

  const { data: agentConfig = null } = useApplicationConfig(app?.slug, selectedEnv);
  const { data: dirtyState } = useDirtyState(selectedEnv, app?.slug);

  // Mutations
  const createApp = useCreateApp();
  const deleteApp = useDeleteApp();
  const uploadJar = useUploadJar();
  const createDeployment = useCreateDeployment();
  const stopDeployment = useStopDeployment();
  const updateContainerConfig = useUpdateContainerConfig();
  const updateAgentConfig = useUpdateApplicationConfig();

  // Form state
  const { form, setForm, reset, serverState } = useDeploymentPageState(
    app,
    agentConfig,
    env?.defaultContainerConfig ?? {},
  );

  // Local UI state
  const [name, setName] = useState('');
  const [stagedJar, setStagedJar] = useState<File | null>(null);
  const [tab, setTab] = useState<TabKey>('monitoring');
  const [deleteConfirm, setDeleteConfirm] = useState(false);
  const [stopTarget, setStopTarget] = useState<string | null>(null);
  const lastDerivedRef = useRef<string>('');

  // Initialize name from app when it loads
  useEffect(() => {
    if (app) setName(app.displayName);
  }, [app?.displayName]); // eslint-disable-line react-hooks/exhaustive-deps

  // Auto-derive name from staged JAR (net-new only, don't overwrite manual edits)
  useEffect(() => {
    if (!stagedJar || app) return;
    const derived = deriveAppName(stagedJar.name);
    if (!name || name === lastDerivedRef.current) {
      setName(derived);
      lastDerivedRef.current = derived;
    }
  }, [stagedJar, app, name]);

  // Auto-switch to Deployment tab when a deployment starts
  useEffect(() => {
    if (activeDeployment) setTab('deployment');
  }, [!!activeDeployment]); // eslint-disable-line react-hooks/exhaustive-deps

  // Derived
  const mode = app ? 'deployed' : 'net-new';
  const dirty = useFormDirty(form, serverState, stagedJar);
  const { dialogOpen: blockerOpen, confirm: blockerConfirm, cancel: blockerCancel } =
    useUnsavedChangesBlocker(dirty.anyLocalEdit);
  const serverDirtyAgainstDeploy = dirtyState?.dirty ?? false;
  const deploymentInProgress = !!activeDeployment;
  const primaryMode = computeMode({
    deploymentInProgress,
    hasLocalEdits: dirty.anyLocalEdit,
    serverDirtyAgainstDeploy,
  });

  // External URL (same formula as IdentitySection)
  const externalUrl = (() => {
    if (!env) return '';
    const slug = app?.slug ?? slugify(name);
    const defaults = env.defaultContainerConfig ?? {};
    const domain = String(defaults.routingDomain ?? '');
    if (defaults.routingMode === 'subdomain' && domain) {
      return `https://${slug || '...'}-${env.slug}.${domain}/`;
    }
    const base = domain ? `https://${domain}` : window.location.origin;
    return `${base}/${env.slug}/${slug || '...'}/`;
  })();

  // ── Tabs definition ────────────────────────────────────────────────
  const tabs: { label: string; value: TabKey }[] = [
    { label: dirty.monitoring ? 'Monitoring *' : 'Monitoring', value: 'monitoring' },
    { label: dirty.resources ? 'Resources *' : 'Resources', value: 'resources' },
    { label: dirty.variables ? 'Variables *' : 'Variables', value: 'variables' },
    { label: dirty.sensitiveKeys ? 'Sensitive Keys *' : 'Sensitive Keys', value: 'sensitive-keys' },
    { label: 'Deployment', value: 'deployment' },
    ...(app
      ? ([
          { label: '● Traces & Taps', value: 'traces' },
          { label: '● Route Recording', value: 'recording' },
        ] as { label: string; value: TabKey }[])
      : []),
  ];

  // ── Handlers ────────────────────────────────────────────────────────

  async function handleSave() {
    const envSlug = selectedEnv!;
    try {
      let targetApp = app;

      // 1. Create app if net-new
      if (!targetApp) {
        targetApp = await createApp.mutateAsync({
          envSlug,
          slug: slugify(name),
          displayName: name.trim(),
        });
      }

      // 2. Upload JAR if staged
      if (stagedJar) {
        await uploadJar.mutateAsync({ envSlug, appSlug: targetApp.slug, file: stagedJar });
      }

      // 3. Save container config
      const r = form.resources;
      const containerConfig: Record<string, unknown> = {
        memoryLimitMb: r.memoryLimit ? parseInt(r.memoryLimit) : null,
        memoryReserveMb: r.memoryReserve ? parseInt(r.memoryReserve) : null,
        cpuRequest: r.cpuRequest ? parseInt(r.cpuRequest) : null,
        cpuLimit: r.cpuLimit ? parseInt(r.cpuLimit) : null,
        exposedPorts: r.ports,
        customEnvVars: Object.fromEntries(
          form.variables.envVars.filter((v) => v.key.trim()).map((v) => [v.key, v.value]),
        ),
        appPort: r.appPort ? parseInt(r.appPort) : 8080,
        replicas: r.replicas ? parseInt(r.replicas) : 1,
        deploymentStrategy: r.deployStrategy,
        stripPathPrefix: r.stripPrefix,
        sslOffloading: r.sslOffloading,
        runtimeType: r.runtimeType,
        customArgs: r.customArgs || null,
        extraNetworks: r.extraNetworks,
      };
      await updateContainerConfig.mutateAsync({ envSlug, appSlug: targetApp.slug, config: containerConfig });

      // 4. Save agent config (staged — applied on next deploy)
      const m = form.monitoring;
      await updateAgentConfig.mutateAsync({
        config: {
          application: targetApp.slug,
          version: agentConfig?.version ?? 0,
          engineLevel: m.engineLevel,
          payloadCaptureMode: m.payloadCaptureMode,
          applicationLogLevel: m.applicationLogLevel,
          agentLogLevel: m.agentLogLevel,
          metricsEnabled: m.metricsEnabled,
          samplingRate: parseFloat(m.samplingRate) || 1.0,
          compressSuccess: m.compressSuccess,
          tracedProcessors: agentConfig?.tracedProcessors ?? {},
          taps: agentConfig?.taps ?? [],
          tapVersion: agentConfig?.tapVersion ?? 0,
          routeRecording: agentConfig?.routeRecording ?? {},
          sensitiveKeys:
            form.sensitiveKeys.sensitiveKeys.length > 0
              ? form.sensitiveKeys.sensitiveKeys
              : undefined,
        },
        environment: envSlug,
        apply: 'staged',
      });

      setStagedJar(null);

      if (!app) {
        // Transition to the existing-app view
        navigate(`/apps/${targetApp.slug}`);
      }
    } catch (e) {
      toast({
        title: 'Save failed',
        description: e instanceof Error ? e.message : 'Unknown error',
        variant: 'error',
        duration: 86_400_000,
      });
    }
  }

  async function handleRedeploy() {
    if (!app) return;
    const envSlug = selectedEnv!;
    setTab('deployment');
    try {
      let versionId: string;

      if (stagedJar) {
        const newVersion = await uploadJar.mutateAsync({ envSlug, appSlug: app.slug, file: stagedJar });
        versionId = newVersion.id;
        setStagedJar(null);
      } else {
        if (!currentVersion) {
          toast({
            title: 'No JAR version available',
            description: 'Upload a JAR before deploying.',
            variant: 'error',
            duration: 86_400_000,
          });
          return;
        }
        versionId = currentVersion.id;
      }

      await createDeployment.mutateAsync({ envSlug, appSlug: app.slug, appVersionId: versionId });
    } catch (e) {
      toast({
        title: 'Redeploy failed',
        description: e instanceof Error ? e.message : 'Unknown error',
        variant: 'error',
        duration: 86_400_000,
      });
    }
  }

  async function handleStop(deploymentId: string) {
    setStopTarget(deploymentId);
  }

  async function confirmStop() {
    if (!stopTarget || !app) return;
    const envSlug = selectedEnv!;
    try {
      await stopDeployment.mutateAsync({ envSlug, appSlug: app.slug, deploymentId: stopTarget });
    } catch (e) {
      toast({
        title: 'Stop failed',
        description: e instanceof Error ? e.message : 'Unknown error',
        variant: 'error',
        duration: 86_400_000,
      });
    } finally {
      setStopTarget(null);
    }
  }

  async function handleDelete() {
    if (!app) return;
    const envSlug = selectedEnv!;
    try {
      await deleteApp.mutateAsync({ envSlug, appSlug: app.slug });
      navigate('/apps');
    } catch (e) {
      toast({
        title: 'Delete failed',
        description: e instanceof Error ? e.message : 'Unknown error',
        variant: 'error',
        duration: 86_400_000,
      });
    }
  }

  function handleRestore(deploymentId: string) {
    const deployment = deployments.find((d) => d.id === deploymentId);
    if (!deployment) return;
    const snap = deployment.deployedConfigSnapshot;
    if (!snap) return;

    setForm((prev) => {
      const a = snap.agentConfig ?? {};
      const c = snap.containerConfig ?? {};
      return {
        monitoring: {
          engineLevel: (a.engineLevel as string) ?? prev.monitoring.engineLevel,
          payloadCaptureMode: (a.payloadCaptureMode as string) ?? prev.monitoring.payloadCaptureMode,
          applicationLogLevel: (a.applicationLogLevel as string) ?? prev.monitoring.applicationLogLevel,
          agentLogLevel: (a.agentLogLevel as string) ?? prev.monitoring.agentLogLevel,
          metricsEnabled: (a.metricsEnabled as boolean) ?? prev.monitoring.metricsEnabled,
          samplingRate: a.samplingRate !== undefined ? String(a.samplingRate) : prev.monitoring.samplingRate,
          compressSuccess: (a.compressSuccess as boolean) ?? prev.monitoring.compressSuccess,
        },
        resources: {
          memoryLimit: c.memoryLimitMb !== undefined ? String(c.memoryLimitMb) : prev.resources.memoryLimit,
          memoryReserve: c.memoryReserveMb != null ? String(c.memoryReserveMb) : prev.resources.memoryReserve,
          cpuRequest: c.cpuRequest !== undefined ? String(c.cpuRequest) : prev.resources.cpuRequest,
          cpuLimit: c.cpuLimit != null ? String(c.cpuLimit) : prev.resources.cpuLimit,
          ports: Array.isArray(c.exposedPorts) ? (c.exposedPorts as number[]) : prev.resources.ports,
          appPort: c.appPort !== undefined ? String(c.appPort) : prev.resources.appPort,
          replicas: c.replicas !== undefined ? String(c.replicas) : prev.resources.replicas,
          deployStrategy: (c.deploymentStrategy as string) ?? prev.resources.deployStrategy,
          stripPrefix: c.stripPathPrefix !== undefined ? (c.stripPathPrefix as boolean) : prev.resources.stripPrefix,
          sslOffloading: c.sslOffloading !== undefined ? (c.sslOffloading as boolean) : prev.resources.sslOffloading,
          runtimeType: (c.runtimeType as string) ?? prev.resources.runtimeType,
          customArgs: c.customArgs !== undefined ? String(c.customArgs ?? '') : prev.resources.customArgs,
          extraNetworks: Array.isArray(c.extraNetworks) ? (c.extraNetworks as string[]) : prev.resources.extraNetworks,
        },
        variables: {
          envVars: c.customEnvVars
            ? Object.entries(c.customEnvVars as Record<string, string>).map(([key, value]) => ({ key, value }))
            : prev.variables.envVars,
        },
        sensitiveKeys: {
          sensitiveKeys: Array.isArray(a.sensitiveKeys)
            ? (a.sensitiveKeys as string[])
            : prev.sensitiveKeys.sensitiveKeys,
        },
      };
    });
  }

  // ── Primary button enabled logic ───────────────────────────────────
  const primaryEnabled = (() => {
    if (primaryMode === 'deploying') return false;
    if (primaryMode === 'save') return !!name.trim() && (isNetNew || dirty.anyLocalEdit);
    return true; // redeploy always enabled
  })();

  // ── Loading guard ──────────────────────────────────────────────────
  if (envLoading || appsLoading) return <PageLoader />;
  if (!env) return <div>Select an environment first.</div>;

  return (
    <div className={styles.container}>
      {/* ── Page header ── */}
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8 }}>
        <h2 style={{ margin: 0 }}>{app ? app.displayName : 'Create Application'}</h2>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
          {dirty.anyLocalEdit && (
            <Button
              size="sm"
              variant="ghost"
              onClick={() => {
                reset();
                setStagedJar(null);
              }}
            >
              Discard
            </Button>
          )}
          <PrimaryActionButton
            mode={primaryMode}
            enabled={primaryEnabled}
            onClick={primaryMode === 'redeploy' ? handleRedeploy : handleSave}
          />
          {app && (
            <Button size="sm" variant="danger" onClick={() => setDeleteConfirm(true)}>
              Delete App
            </Button>
          )}
        </div>
      </div>

      {/* ── Identity & Artifact ── */}
      <IdentitySection
        mode={mode}
        environment={env}
        app={app}
        currentVersion={currentVersion}
        name={name}
        onNameChange={setName}
        stagedJar={stagedJar}
        onStagedJarChange={setStagedJar}
        deploying={deploymentInProgress}
      />

      {/* ── Checkpoints (deployed apps only) ── */}
      {app && (
        <Checkpoints
          deployments={deployments}
          versions={versions}
          currentDeploymentId={currentDeployment?.id ?? null}
          onRestore={handleRestore}
        />
      )}

      {/* ── Config tabs ── */}
      <Tabs
        tabs={tabs}
        active={tab}
        onChange={(v) => setTab(v as TabKey)}
      />

      <div className={styles.section} style={{ flex: '1 1 auto', minHeight: 0 }}>
        {tab === 'monitoring' && (
          <MonitoringTab
            value={form.monitoring}
            onChange={(next) => setForm((prev) => ({ ...prev, monitoring: next }))}
            disabled={deploymentInProgress}
          />
        )}
        {tab === 'resources' && (
          <ResourcesTab
            value={form.resources}
            onChange={(next) => setForm((prev) => ({ ...prev, resources: next }))}
            disabled={deploymentInProgress}
            isProd={env.production ?? false}
          />
        )}
        {tab === 'variables' && (
          <VariablesTab
            value={form.variables}
            onChange={(next) => setForm((prev) => ({ ...prev, variables: next }))}
            disabled={deploymentInProgress}
          />
        )}
        {tab === 'sensitive-keys' && (
          <SensitiveKeysTab
            value={form.sensitiveKeys}
            onChange={(next) => setForm((prev) => ({ ...prev, sensitiveKeys: next }))}
            disabled={deploymentInProgress}
          />
        )}
        {tab === 'deployment' && app && (
          <DeploymentTab
            deployments={deployments}
            versions={versions}
            appSlug={app.slug}
            envSlug={env.slug}
            externalUrl={externalUrl}
            onStop={handleStop}
            onStart={(deploymentId) => {
              // Re-deploy from a specific historical deployment's version
              const d = deployments.find((dep) => dep.id === deploymentId);
              if (d && selectedEnv && app) {
                setTab('deployment');
                createDeployment.mutateAsync({
                  envSlug: selectedEnv,
                  appSlug: app.slug,
                  appVersionId: d.appVersionId,
                }).catch((e: unknown) =>
                  toast({
                    title: 'Start failed',
                    description: e instanceof Error ? e.message : 'Unknown error',
                    variant: 'error',
                    duration: 86_400_000,
                  }),
                );
              }
            }}
          />
        )}
        {tab === 'deployment' && !app && (
          <div style={{ color: 'var(--text-muted)', fontSize: 14, padding: 16 }}>
            Save the app first to see deployment status.
          </div>
        )}
        {tab === 'traces' && app && (
          <TracesTapsTab app={app} environment={env} />
        )}
        {tab === 'recording' && app && (
          <RouteRecordingTab app={app} environment={env} />
        )}
      </div>

      {/* ── Stop confirmation dialog ── */}
      <AlertDialog
        open={!!stopTarget}
        onClose={() => setStopTarget(null)}
        onConfirm={confirmStop}
        title="Stop deployment?"
        description="This will stop the running container. The app will be unavailable until redeployed."
        confirmLabel="Stop"
        variant="danger"
      />

      {/* ── Delete confirmation dialog ── */}
      <AlertDialog
        open={deleteConfirm}
        onClose={() => setDeleteConfirm(false)}
        onConfirm={() => {
          setDeleteConfirm(false);
          handleDelete();
        }}
        title={`Delete "${app?.displayName ?? ''}"?`}
        description="This permanently removes the app, all versions, and all deployments. This cannot be undone."
        confirmLabel="Delete"
        variant="danger"
      />

      {/* ── Unsaved changes navigation blocker ── */}
      <AlertDialog
        open={blockerOpen}
        onClose={blockerCancel}
        onConfirm={blockerConfirm}
        title="Unsaved changes"
        description="You have unsaved changes on this page. Discard and leave?"
        confirmLabel="Discard & Leave"
        variant="warning"
      />
    </div>
  );
}
