import { useState, useEffect, useRef } from 'react';
import { useParams, useLocation, useNavigate } from 'react-router';
import { useQueryClient } from '@tanstack/react-query';
import { AlertDialog, Badge, Button, StatusDot, 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 { CheckpointsTable } from './CheckpointsTable';
import { CheckpointDetailDrawer } from './CheckpointDetailDrawer';
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 { snapshotToForm } from './CheckpointDetailDrawer/snapshotToForm';
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';

const STATUS_COLORS: Record<string, 'success' | 'warning' | 'error' | 'auto' | 'running'> = {
  RUNNING: 'running', STARTING: 'warning', FAILED: 'error', STOPPED: 'auto',
  DEGRADED: 'warning', STOPPING: 'auto',
};

const DEPLOY_STATUS_DOT: Record<string, 'live' | 'stale' | 'dead' | 'success' | 'warning' | 'error' | 'running'> = {
  RUNNING: 'live', STARTING: 'running', DEGRADED: 'stale',
  STOPPING: 'stale', STOPPED: 'dead', FAILED: 'error',
};

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 queryClient = useQueryClient();
  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 latestDeployment = deployments
    .slice()
    .sort((a, b) => (b.createdAt ?? '').localeCompare(a.createdAt ?? ''))[0] ?? null;

  const { data: agentConfig = null } = useApplicationConfig(app?.slug, selectedEnv);
  const { data: dirtyState, isLoading: dirtyLoading } = 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 [uploadPct, setUploadPct] = useState<number | null>(null);
  const [tab, setTab] = useState<TabKey>('monitoring');
  const [deleteConfirm, setDeleteConfirm] = useState(false);
  const [stopTarget, setStopTarget] = useState<string | null>(null);
  const [selectedCheckpointId, setSelectedCheckpointId] = 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);
  // Before the first dirty-state fetch resolves, default to "not dirty" so the
  // primary button shows `Save (disabled)` — not a stale `Redeploy`. Once loaded,
  // fall back to `true` if the endpoint failed entirely (fail-safe for the
  // redeploy path).
  const serverDirtyAgainstDeploy = app && dirtyLoading ? false : (dirtyState?.dirty ?? true);
  const deploymentInProgress = !!activeDeployment;
  const uploading = uploadPct !== null;
  const primaryMode = computeMode({
    deploymentInProgress,
    uploading,
    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) {
        setUploadPct(0);
        try {
          await uploadJar.mutateAsync({
            envSlug,
            appSlug: targetApp.slug,
            file: stagedJar,
            onProgress: setUploadPct,
          });
        } finally {
          setUploadPct(null);
        }
      }

      // 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,
        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,
        externalRouting: r.externalRouting,
        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);

      toast({ title: 'Configuration saved', variant: 'success' });

      // Invalidate dirty-state so the button reflects the new saved state
      await queryClient.invalidateQueries({ queryKey: ['apps', envSlug, targetApp.slug, 'dirty-state'] });

      if (!app) {
        // Transition to the existing-app view — refetch apps first so the new app
        // is in the cache before the router renders the deployed view (prevents
        // the transient Save-disabled flash while useApps is loading).
        await queryClient.refetchQueries({ queryKey: ['apps', envSlug] });
        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) {
        setUploadPct(0);
        try {
          const newVersion = await uploadJar.mutateAsync({
            envSlug,
            appSlug: app.slug,
            file: stagedJar,
            onProgress: setUploadPct,
          });
          versionId = newVersion.id;
        } finally {
          setUploadPct(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 });
      setStagedJar(null);
      // Invalidate dirty-state and versions so button recomputes after deploy
      queryClient.invalidateQueries({ queryKey: ['apps', envSlug, app.slug, 'dirty-state'] });
      queryClient.invalidateQueries({ queryKey: ['apps', envSlug, app.slug, 'versions'] });
    } 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) {
      toast({
        title: 'Cannot restore checkpoint',
        description: 'This checkpoint predates snapshotting and cannot be restored.',
        variant: 'warning',
      });
      return;
    }

    setForm((prev) => snapshotToForm(snap, prev));
  }

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

  // Checkpoint drawer derivations
  const jarRetentionCount = env?.jarRetentionCount ?? null;
  const selectedDep = selectedCheckpointId
    ? deployments.find((d) => d.id === selectedCheckpointId) ?? null
    : null;
  const selectedDepVersion = selectedDep
    ? versions.find((v) => v.id === selectedDep.appVersionId)
    : undefined;

  // ── 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 }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
          <h2 style={{ margin: 0 }}>{app ? app.displayName : 'Create Application'}</h2>
          {app && latestDeployment && (
            <span style={{ display: 'inline-flex', alignItems: 'center', gap: 5 }}>
              <StatusDot variant={DEPLOY_STATUS_DOT[latestDeployment.status] ?? 'dead'} />
              <Badge
                label={latestDeployment.status}
                color={STATUS_COLORS[latestDeployment.status] ?? 'auto'}
              />
            </span>
          )}
          {app && !deploymentInProgress && (dirty.anyLocalEdit || serverDirtyAgainstDeploy) && (() => {
            const diffs = dirtyState?.differences ?? [];
            const noSnapshot = diffs.length === 1 && diffs[0].field === 'snapshot';
            const tooltip = dirty.anyLocalEdit
              ? 'Local edits not yet saved — see tabs marked with *.'
              : noSnapshot
                ? 'No successful deployment recorded for this app yet.'
                : diffs.length > 0
                  ? `Differs from last successful deploy:\n` +
                    diffs.map((d) => `• ${d.field}\n    staged:   ${d.staged}\n    deployed: ${d.deployed}`).join('\n')
                  : 'Server reports config differs from last successful deploy.';
            return (
              <span title={tooltip} style={{ display: 'inline-flex' }}>
                <Badge
                  label={dirty.anyLocalEdit ? 'Pending deploy' : `Pending deploy (${diffs.length})`}
                  color="warning"
                />
              </span>
            );
          })()}
        </div>
        <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}
            progress={uploadPct ?? undefined}
            onClick={primaryMode === 'redeploy' ? handleRedeploy : handleSave}
          />
          {app && latestDeployment && (
            latestDeployment.status === 'RUNNING'
              || latestDeployment.status === 'STARTING'
              || latestDeployment.status === 'DEGRADED'
          ) && (
            <Button size="sm" variant="danger" onClick={() => handleStop(latestDeployment.id)}>
              Stop
            </Button>
          )}
          {app && latestDeployment && latestDeployment.status === 'STOPPED' && (
            <Button
              size="sm"
              variant="secondary"
              onClick={() => {
                setTab('deployment');
                createDeployment
                  .mutateAsync({
                    envSlug: selectedEnv!,
                    appSlug: app.slug,
                    appVersionId: latestDeployment.appVersionId,
                  })
                  .catch((e: unknown) =>
                    toast({
                      title: 'Start failed',
                      description: e instanceof Error ? e.message : 'Unknown error',
                      variant: 'error',
                      duration: 86_400_000,
                    }),
                  );
              }}
            >
              Start
            </Button>
          )}
          {app && (
            <Button size="sm" variant="danger" onClick={() => setDeleteConfirm(true)}>
              Delete App
            </Button>
          )}
        </div>
      </div>

      {/* ── Identity & Artifact (with Checkpoints for deployed apps) ── */}
      <IdentitySection
        mode={mode}
        environment={env}
        app={app}
        currentVersion={currentVersion}
        name={name}
        onNameChange={setName}
        stagedJar={stagedJar}
        onStagedJarChange={setStagedJar}
        deploying={deploymentInProgress}
        checkpointsSlot={
          app ? (
            <CheckpointsTable
              deployments={deployments}
              versions={versions}
              currentDeploymentId={currentDeployment?.id ?? null}
              jarRetentionCount={jarRetentionCount}
              onSelect={setSelectedCheckpointId}
            />
          ) : undefined
        }
      >
        {app && selectedDep && (
          <CheckpointDetailDrawer
            open
            onClose={() => setSelectedCheckpointId(null)}
            deployment={selectedDep}
            version={selectedDepVersion}
            appSlug={app.slug}
            envSlug={selectedEnv ?? ''}
            currentForm={form}
            onRestore={(deploymentId) => {
              handleRestore(deploymentId);
              setSelectedCheckpointId(null);
            }}
          />
        )}
      </IdentitySection>

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

      <div className={styles.tabContent}>
        {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}
          />
        )}
        {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>
      </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>
  );
}
