import { useState, useMemo, useRef } from 'react';
import { useParams } from 'react-router';
import {
  Badge,
  Button,
  DataTable,
  Input,
  MonoText,
  SectionHeader,
  Spinner,
  useToast,
} from '@cameleer/design-system';
import type { Column } from '@cameleer/design-system';
import { useEnvironmentStore } from '../../api/environment-store';
import { useEnvironments } from '../../api/queries/admin/environments';
import {
  useAllApps,
  useApps,
  useCreateApp,
  useDeleteApp,
  useAppVersions,
  useUploadJar,
  useDeployments,
  useCreateDeployment,
  useStopDeployment,
  useUpdateContainerConfig,
} from '../../api/queries/admin/apps';
import type { App, AppVersion, Deployment } from '../../api/queries/admin/apps';
import type { Environment } from '../../api/queries/admin/environments';
import styles from './AppsTab.module.css';

function formatBytes(bytes: number): string {
  if (bytes >= 1_048_576) return `${(bytes / 1_048_576).toFixed(1)} MB`;
  if (bytes >= 1024) return `${(bytes / 1024).toFixed(1)} KB`;
  return `${bytes} B`;
}

function timeAgo(date: string): string {
  const seconds = Math.floor((Date.now() - new Date(date).getTime()) / 1000);
  if (seconds < 60) return `${seconds}s ago`;
  const minutes = Math.floor(seconds / 60);
  if (minutes < 60) return `${minutes}m ago`;
  const hours = Math.floor(minutes / 60);
  if (hours < 24) return `${hours}h ago`;
  const days = Math.floor(hours / 24);
  return `${days}d ago`;
}

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

export default function AppsTab() {
  const { appId } = useParams<{ appId?: string }>();
  const selectedEnv = useEnvironmentStore((s) => s.environment);
  const { data: environments = [] } = useEnvironments();

  // If an app is selected via sidebar, show detail view
  if (appId) {
    return (
      <AppDetailView
        appId={appId}
        environments={environments}
      />
    );
  }

  // Otherwise show list view
  return (
    <AppListView
      selectedEnv={selectedEnv}
      environments={environments}
    />
  );
}

// --- List View ---

function AppListView({
  selectedEnv,
  environments,
}: {
  selectedEnv: string | undefined;
  environments: Environment[];
}) {
  const { toast } = useToast();
  const { data: allApps = [], isLoading: allLoading } = useAllApps();
  const { data: envApps = [], isLoading: envLoading } = useApps(
    selectedEnv ? environments.find((e) => e.slug === selectedEnv)?.id : undefined,
  );

  const [creating, setCreating] = useState(false);
  const [newSlug, setNewSlug] = useState('');
  const [newDisplayName, setNewDisplayName] = useState('');
  const [newEnvId, setNewEnvId] = useState('');
  const createApp = useCreateApp();

  const apps = selectedEnv ? envApps : allApps;
  const isLoading = selectedEnv ? envLoading : allLoading;

  // Build enriched rows with environment name and latest deployment status
  const envMap = useMemo(() => {
    const m = new Map<string, Environment>();
    for (const e of environments) m.set(e.id, e);
    return m;
  }, [environments]);

  type AppRow = App & { envName: string };
  const rows: AppRow[] = useMemo(
    () => apps.map((a) => ({
      ...a,
      envName: envMap.get(a.environmentId)?.displayName ?? '?',
    })),
    [apps, envMap],
  );

  const columns: Column<AppRow>[] = useMemo(() => [
    {
      key: 'displayName',
      header: 'Name',
      render: (_val: unknown, row: AppRow) => (
        <div>
          <div className={styles.cellName}>{row.displayName}</div>
          <div className={styles.cellMeta}>{row.slug}</div>
        </div>
      ),
      sortable: true,
    },
    ...(!selectedEnv ? [{
      key: 'envName',
      header: 'Environment',
      render: (_val: unknown, row: AppRow) => <Badge label={row.envName} color={'auto' as const} />,
      sortable: true,
    }] : []),
    {
      key: 'updatedAt',
      header: 'Updated',
      render: (_val: unknown, row: AppRow) => <span className={styles.cellMeta}>{timeAgo(row.updatedAt)}</span>,
      sortable: true,
    },
    {
      key: 'createdAt',
      header: 'Created',
      render: (_val: unknown, row: AppRow) => <span className={styles.cellMeta}>{new Date(row.createdAt).toLocaleDateString()}</span>,
      sortable: true,
    },
  ], [selectedEnv]);

  async function handleCreate() {
    if (!newSlug.trim() || !newDisplayName.trim() || !newEnvId) return;
    try {
      await createApp.mutateAsync({
        environmentId: newEnvId,
        slug: newSlug.trim(),
        displayName: newDisplayName.trim(),
      });
      toast({ title: 'App created', description: newSlug.trim(), variant: 'success' });
      setCreating(false);
      setNewSlug('');
      setNewDisplayName('');
      setNewEnvId('');
    } catch {
      toast({ title: 'Failed to create app', variant: 'error', duration: 86_400_000 });
    }
  }

  if (isLoading) return <Spinner size="md" />;

  return (
    <div className={styles.container}>
      <div className={styles.toolbar}>
        <Button size="sm" variant="primary" onClick={() => {
          if (!newEnvId && environments.length > 0) setNewEnvId(environments[0].id);
          setCreating(!creating);
        }}>
          + Create App
        </Button>
      </div>

      {creating && (
        <div className={styles.createForm}>
          <select
            className={styles.envSelect}
            value={newEnvId}
            onChange={(e) => setNewEnvId(e.target.value)}
          >
            {environments.map((env) => (
              <option key={env.id} value={env.id}>{env.displayName} ({env.slug})</option>
            ))}
          </select>
          <Input placeholder="Slug *" value={newSlug} onChange={(e) => setNewSlug(e.target.value)} />
          <Input placeholder="Display name *" value={newDisplayName} onChange={(e) => setNewDisplayName(e.target.value)} />
          <div className={styles.createActions}>
            <Button size="sm" variant="ghost" onClick={() => setCreating(false)}>Cancel</Button>
            <Button
              size="sm"
              variant="primary"
              onClick={handleCreate}
              loading={createApp.isPending}
              disabled={!newSlug.trim() || !newDisplayName.trim() || !newEnvId}
            >
              Create
            </Button>
          </div>
        </div>
      )}

      <DataTable
        columns={columns}
        data={rows}
        onRowClick={(row) => {
          window.location.href = `${window.location.pathname.replace(/\/apps.*/, '')}/apps/${row.id}`;
        }}
      />
    </div>
  );
}

// --- Detail View ---

function AppDetailView({
  appId,
  environments,
}: {
  appId: string;
  environments: Environment[];
}) {
  const { toast } = useToast();
  const { data: allApps = [] } = useAllApps();
  const app = useMemo(() => allApps.find((a) => a.id === appId), [allApps, appId]);
  const { data: versions = [] } = useAppVersions(appId);
  const { data: deployments = [] } = useDeployments(appId);
  const uploadJar = useUploadJar();
  const createDeployment = useCreateDeployment();
  const stopDeployment = useStopDeployment();
  const deleteApp = useDeleteApp();
  const fileInputRef = useRef<HTMLInputElement>(null);

  const envMap = useMemo(() => {
    const m = new Map<string, Environment>();
    for (const e of environments) m.set(e.id, e);
    return m;
  }, [environments]);

  const sortedVersions = useMemo(
    () => [...versions].sort((a, b) => b.version - a.version),
    [versions],
  );

  if (!app) return <Spinner size="md" />;

  const envName = envMap.get(app.environmentId)?.displayName ?? '?';

  async function handleUpload(e: React.ChangeEvent<HTMLInputElement>) {
    const file = e.target.files?.[0];
    if (!file) return;
    try {
      const v = await uploadJar.mutateAsync({ appId, file });
      toast({ title: `Version ${v.version} uploaded`, description: file.name, variant: 'success' });
    } catch {
      toast({ title: 'Upload failed', variant: 'error', duration: 86_400_000 });
    }
    if (fileInputRef.current) fileInputRef.current.value = '';
  }

  async function handleDeploy(versionId: string, environmentId: string) {
    try {
      await createDeployment.mutateAsync({ appId, appVersionId: versionId, environmentId });
      toast({ title: 'Deployment started', variant: 'success' });
    } catch {
      toast({ title: 'Deploy failed', variant: 'error', duration: 86_400_000 });
    }
  }

  async function handleStop(deploymentId: string) {
    try {
      await stopDeployment.mutateAsync({ appId, deploymentId });
      toast({ title: 'Deployment stopped', variant: 'warning' });
    } catch {
      toast({ title: 'Stop failed', variant: 'error', duration: 86_400_000 });
    }
  }

  async function handleDelete() {
    try {
      await deleteApp.mutateAsync(appId);
      toast({ title: 'App deleted', variant: 'warning' });
      window.history.back();
    } catch {
      toast({ title: 'Delete failed', variant: 'error', duration: 86_400_000 });
    }
  }

  return (
    <div className={styles.container}>
      <div className={styles.detailHeader}>
        <div>
          <h2 className={styles.detailTitle}>{app.displayName}</h2>
          <div className={styles.detailMeta}>
            {app.slug} &middot; <Badge label={envName} color="auto" />
          </div>
        </div>
        <Button size="sm" variant="danger" onClick={handleDelete}>Delete</Button>
      </div>

      <div className={styles.metaGrid}>
        <span className={styles.metaLabel}>ID</span>
        <MonoText size="xs">{app.id}</MonoText>
        <span className={styles.metaLabel}>Environment</span>
        <span>{envName}</span>
        <span className={styles.metaLabel}>Created</span>
        <span>{new Date(app.createdAt).toLocaleDateString()}</span>
        <span className={styles.metaLabel}>Updated</span>
        <span>{timeAgo(app.updatedAt)}</span>
      </div>

      {/* Deployments across all environments */}
      <SectionHeader>Deployments ({deployments.length})</SectionHeader>
      {deployments.length === 0 && <p className={styles.emptyNote}>No deployments yet.</p>}
      {deployments.map((d) => (
        <DeploymentRow
          key={d.id}
          deployment={d}
          versions={versions}
          envMap={envMap}
          onStop={() => handleStop(d.id)}
        />
      ))}

      {/* Versions */}
      <SectionHeader>Versions ({versions.length})</SectionHeader>
      <div style={{ marginBottom: 8 }}>
        <input ref={fileInputRef} type="file" accept=".jar" style={{ display: 'none' }} onChange={handleUpload} />
        <Button size="sm" variant="secondary" onClick={() => fileInputRef.current?.click()} loading={uploadJar.isPending}>
          Upload JAR
        </Button>
      </div>
      {sortedVersions.length === 0 && <p className={styles.emptyNote}>No versions uploaded yet.</p>}
      {sortedVersions.map((v) => (
        <VersionRow
          key={v.id}
          version={v}
          environments={environments}
          onDeploy={(envId) => handleDeploy(v.id, envId)}
        />
      ))}

      {/* Container Config */}
      <SectionHeader>Container Configuration</SectionHeader>
      <ContainerConfigForm app={app} environment={envMap.get(app.environmentId)} />
    </div>
  );
}

// --- Deployment Row ---

function DeploymentRow({
  deployment,
  versions,
  envMap,
  onStop,
}: {
  deployment: Deployment;
  versions: AppVersion[];
  envMap: Map<string, Environment>;
  onStop: () => void;
}) {
  const version = versions.find((v) => v.id === deployment.appVersionId);
  const env = envMap.get(deployment.environmentId);
  const canStop = deployment.status === 'RUNNING' || deployment.status === 'STARTING';

  return (
    <div className={styles.row}>
      <Badge label={deployment.status} color={STATUS_COLORS[deployment.status] ?? 'auto'} />
      <Badge label={env?.displayName ?? '?'} color="auto" />
      <span className={styles.rowText}>{version ? `v${version.version}` : '?'}</span>
      {deployment.containerName && <MonoText size="xs">{deployment.containerName}</MonoText>}
      {deployment.errorMessage && <span className={styles.errorText}>{deployment.errorMessage}</span>}
      <span className={styles.rowMeta}>{deployment.deployedAt ? timeAgo(deployment.deployedAt) : ''}</span>
      {canStop && <Button size="sm" variant="danger" onClick={onStop}>Stop</Button>}
    </div>
  );
}

// --- Version Row ---

function VersionRow({
  version,
  environments,
  onDeploy,
}: {
  version: AppVersion;
  environments: Environment[];
  onDeploy: (environmentId: string) => void;
}) {
  const [deployEnv, setDeployEnv] = useState('');

  return (
    <div className={styles.row}>
      <Badge label={`v${version.version}`} color="auto" />
      <span className={styles.rowText}>
        {version.jarFilename} ({formatBytes(version.jarSizeBytes)})
      </span>
      <span className={styles.rowMeta}>{timeAgo(version.uploadedAt)}</span>
      <select
        className={styles.envSelect}
        value={deployEnv}
        onChange={(e) => setDeployEnv(e.target.value)}
      >
        <option value="">Deploy to...</option>
        {environments.filter((e) => e.enabled).map((e) => (
          <option key={e.id} value={e.id}>{e.displayName}</option>
        ))}
      </select>
      <Button
        size="sm"
        variant="secondary"
        disabled={!deployEnv}
        onClick={() => { onDeploy(deployEnv); setDeployEnv(''); }}
      >
        Deploy
      </Button>
    </div>
  );
}

// --- Container Config Form ---

function ContainerConfigForm({ app, environment }: { app: App; environment?: Environment }) {
  const { toast } = useToast();
  const updateConfig = useUpdateContainerConfig();
  const isProd = environment?.production ?? false;

  const defaults = environment?.defaultContainerConfig ?? {};
  const merged = { ...defaults, ...app.containerConfig };

  const [memoryLimitMb, setMemoryLimitMb] = useState<string>(String(merged.memoryLimitMb ?? '512'));
  const [memoryReserveMb, setMemoryReserveMb] = useState<string>(String(merged.memoryReserveMb ?? ''));
  const [cpuShares, setCpuShares] = useState<string>(String(merged.cpuShares ?? '512'));
  const [cpuLimit, setCpuLimit] = useState<string>(String(merged.cpuLimit ?? ''));
  const [exposedPorts, setExposedPorts] = useState<string>(
    Array.isArray(merged.exposedPorts) ? (merged.exposedPorts as number[]).join(', ') : '',
  );
  const [customEnvVars, setCustomEnvVars] = useState<string>(
    merged.customEnvVars ? JSON.stringify(merged.customEnvVars, null, 2) : '{}',
  );

  async function handleSave() {
    const config: Record<string, unknown> = {
      memoryLimitMb: memoryLimitMb ? parseInt(memoryLimitMb) : null,
      memoryReserveMb: memoryReserveMb ? parseInt(memoryReserveMb) : null,
      cpuShares: cpuShares ? parseInt(cpuShares) : null,
      cpuLimit: cpuLimit ? parseFloat(cpuLimit) : null,
      exposedPorts: exposedPorts ? exposedPorts.split(',').map((p) => parseInt(p.trim())).filter((p) => !isNaN(p)) : [],
    };
    try {
      config.customEnvVars = JSON.parse(customEnvVars);
    } catch {
      toast({ title: 'Invalid JSON in environment variables', variant: 'error' });
      return;
    }
    try {
      await updateConfig.mutateAsync({ appId: app.id, config });
      toast({ title: 'Container config saved', variant: 'success' });
    } catch {
      toast({ title: 'Failed to save config', variant: 'error', duration: 86_400_000 });
    }
  }

  return (
    <div className={styles.configForm}>
      <div className={styles.configSection}>
        <h4 className={styles.configTitle}>Resources</h4>
        <div className={styles.configGrid}>
          <label className={styles.configLabel}>Memory Limit (MB)</label>
          <Input value={memoryLimitMb} onChange={(e) => setMemoryLimitMb(e.target.value)} placeholder="512" />
          <label className={styles.configLabel}>Memory Reserve (MB)</label>
          <div className={styles.configField}>
            <Input
              value={memoryReserveMb}
              onChange={(e) => setMemoryReserveMb(e.target.value)}
              placeholder="—"
              disabled={!isProd}
            />
            {!isProd && <span className={styles.configHint}>Available in production environments only</span>}
          </div>
          <label className={styles.configLabel}>CPU Shares</label>
          <Input value={cpuShares} onChange={(e) => setCpuShares(e.target.value)} placeholder="512" />
          <label className={styles.configLabel}>CPU Limit (cores)</label>
          <Input value={cpuLimit} onChange={(e) => setCpuLimit(e.target.value)} placeholder="e.g. 1.0" />
          <label className={styles.configLabel}>Exposed Ports</label>
          <Input value={exposedPorts} onChange={(e) => setExposedPorts(e.target.value)} placeholder="e.g. 8080, 9090" />
        </div>
      </div>

      <div className={styles.configSection}>
        <h4 className={styles.configTitle}>Custom Environment Variables</h4>
        <textarea
          className={styles.envVarEditor}
          value={customEnvVars}
          onChange={(e) => setCustomEnvVars(e.target.value)}
          rows={6}
          spellCheck={false}
        />
      </div>

      <Button variant="primary" onClick={handleSave} loading={updateConfig.isPending}>
        Save Configuration
      </Button>
    </div>
  );
}
