import { useEffect, useState } from 'react';
import { useParams, useNavigate } from 'react-router';
import { useQuery, useMutation } from '@tanstack/react-query';
import { api } from '@/lib/api';
import { formatBytes } from '@/lib/utils';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from '@/components/ui/select';

interface Game {
  environmentVars?: GameEnvironmentVar[];
  id: string;
  name: string;
  slug: string;
  dockerImage: string;
}

interface GameEnvironmentVar {
  key: string;
  label?: string;
  default?: string;
  description?: string;
  required?: boolean;
  inputType?: 'text' | 'boolean';
  composeInto?: string;
  enabledLabel?: string;
  disabledLabel?: string;
}

interface Node {
  id: string;
  name: string;
  fqdn: string;
  memoryTotal: number;
  diskTotal: number;
}

interface Allocation {
  id: string;
  ip: string;
  port: number;
  serverId: string | null;
}

interface PaginatedResponse<T> {
  data: T[];
  meta: { total: number };
}

interface AdditionalPortRequirement {
  key: string;
  label: string;
  defaultPort: number;
  protocols: Array<'tcp' | 'udp'>;
  description: string;
}

function additionalPortRequirementsForGame(gameSlug: string): AdditionalPortRequirement[] {
  const slug = gameSlug.trim().toLowerCase();

  if (slug === 'satisfactory') {
    return [
      {
        key: 'satisfactory-messaging',
        label: 'Messaging Port',
        defaultPort: 8888,
        protocols: ['tcp'],
        description: 'Required by the Satisfactory server messaging API.',
      },
    ];
  }

  if (slug === 'ark-se') {
    return [
      {
        key: 'ark-raw-udp',
        label: 'Raw UDP Socket Port',
        defaultPort: 7778,
        protocols: ['udp'],
        description: 'ARK opens a second UDP socket, normally the game port + 1.',
      },
      {
        key: 'ark-query',
        label: 'Steam Query Port',
        defaultPort: 27015,
        protocols: ['udp'],
        description: 'Used by the Steam server browser to list the server.',
      },
      {
        key: 'ark-rcon',
        label: 'RCON Port',
        defaultPort: 27020,
        protocols: ['tcp'],
        description: 'Console commands and the player list are sent over RCON.',
      },
    ];
  }

  return [];
}

export function CreateServerPage() {
  const { orgId } = useParams();
  const navigate = useNavigate();

  const [step, setStep] = useState(1);
  const [name, setName] = useState('');
  const [description, setDescription] = useState('');
  const [gameId, setGameId] = useState('');
  const [nodeId, setNodeId] = useState('');
  const [allocationId, setAllocationId] = useState('');
  const [additionalAllocationIds, setAdditionalAllocationIds] = useState<Record<string, string>>(
    {},
  );
  const [memoryLimit, setMemoryLimit] = useState(1024);
  const [diskLimit, setDiskLimit] = useState(5120);
  const [cpuLimit, setCpuLimit] = useState(100);
  const [environment, setEnvironment] = useState<Record<string, string>>({});

  const { data: gamesData } = useQuery({
    queryKey: ['games'],
    queryFn: () => api.get<PaginatedResponse<Game>>('/games'),
  });

  const { data: nodesData } = useQuery({
    queryKey: ['nodes', orgId],
    queryFn: () => api.get<PaginatedResponse<Node>>(`/organizations/${orgId}/nodes`),
  });

  const { data: allocationsData } = useQuery({
    queryKey: ['allocations', orgId, nodeId],
    queryFn: () =>
      api.get<PaginatedResponse<Allocation>>(`/organizations/${orgId}/nodes/${nodeId}/allocations`),
    enabled: !!nodeId,
  });

  const freeAllocations = (allocationsData?.data ?? []).filter((a) => !a.serverId);

  const createMutation = useMutation({
    mutationFn: (body: Record<string, unknown>) =>
      api.post(`/organizations/${orgId}/servers`, body),
    onSuccess: () => {
      navigate(`/org/${orgId}/dashboard`);
    },
  });

  const games = gamesData?.data ?? [];
  const nodes = nodesData?.data ?? [];
  const activeGame = games.find((game) => game.id === gameId);
  const additionalPortRequirements = activeGame
    ? additionalPortRequirementsForGame(activeGame.slug)
    : [];
  const visibleEnvironmentVars = (activeGame?.environmentVars ?? []).filter((variable) => {
    const key = variable.key?.trim();
    return Boolean(key) && !variable.composeInto?.trim();
  });
  const missingRequiredEnvironment = visibleEnvironmentVars.some((variable) => {
    const key = variable.key.trim();
    const currentValue = environment[key] ?? String(variable.default ?? '');
    return Boolean(variable.required) && !currentValue.trim();
  });
  const missingRequiredAdditionalPorts = additionalPortRequirements.some(
    (requirement) => !additionalAllocationIds[requirement.key],
  );

  useEffect(() => {
    if (!activeGame) {
      setEnvironment({});
      return;
    }

    setEnvironment((current) => {
      const next: Record<string, string> = {};
      for (const variable of activeGame.environmentVars ?? []) {
        const key = variable.key?.trim();
        if (!key || variable.composeInto?.trim()) continue;
        next[key] = current[key] ?? String(variable.default ?? '');
      }
      return next;
    });
    setAdditionalAllocationIds({});
    if (activeGame.slug.trim().toLowerCase() === 'satisfactory') {
      setMemoryLimit((current) => Math.max(current, 8192));
      setDiskLimit((current) => Math.max(current, 12288));
    }
  }, [activeGame?.id]);

  useEffect(() => {
    setAdditionalAllocationIds((current) => {
      const allowedKeys = new Set(additionalPortRequirements.map((requirement) => requirement.key));
      const next = Object.fromEntries(
        Object.entries(current).filter(([key]) => allowedKeys.has(key)),
      );
      return next;
    });
  }, [activeGame?.slug]);

  const handleCreate = () => {
    const environmentPayload = Object.fromEntries(
      Object.entries(environment).filter(([, value]) => value.trim() !== ''),
    );

    createMutation.mutate({
      name,
      description: description || undefined,
      gameId,
      nodeId,
      allocationId,
      memoryLimit: memoryLimit * 1024 * 1024,
      diskLimit: diskLimit * 1024 * 1024,
      cpuLimit,
      additionalAllocationIds:
        additionalPortRequirements.length > 0
          ? additionalPortRequirements
              .map((requirement) => additionalAllocationIds[requirement.key])
              .filter(Boolean)
          : undefined,
      environment: Object.keys(environmentPayload).length > 0 ? environmentPayload : undefined,
    });
  };

  const allocationOptionsForRequirement = (requirementKey: string) => {
    const selectedByOtherRequirements = new Set(
      Object.entries(additionalAllocationIds)
        .filter(([key]) => key !== requirementKey)
        .map(([, id]) => id)
        .filter(Boolean),
    );

    return freeAllocations.filter(
      (allocation) =>
        allocation.id !== allocationId && !selectedByOtherRequirements.has(allocation.id),
    );
  };

  return (
    <div className="mx-auto max-w-2xl space-y-6">
      <div>
        <h1 className="text-2xl font-bold">Create Server</h1>
        <p className="text-muted-foreground">Set up a new game server</p>
      </div>

      <div className="flex gap-2">
        {[1, 2, 3].map((s) => (
          <div
            key={s}
            className={`h-1.5 flex-1 rounded-full ${s <= step ? 'bg-primary' : 'bg-muted'}`}
          />
        ))}
      </div>

      {step === 1 && (
        <Card>
          <CardHeader>
            <CardTitle>Basic Information</CardTitle>
            <CardDescription>Choose a name and game for your server</CardDescription>
          </CardHeader>
          <CardContent className="space-y-4">
            <div className="space-y-2">
              <Label>Server Name</Label>
              <Input
                value={name}
                onChange={(e) => setName(e.target.value)}
                placeholder="My Awesome Server"
              />
            </div>
            <div className="space-y-2">
              <Label>Description (optional)</Label>
              <Input
                value={description}
                onChange={(e) => setDescription(e.target.value)}
                placeholder="A short description"
              />
            </div>
            <div className="space-y-2">
              <Label>Game</Label>
              <Select value={gameId} onValueChange={setGameId}>
                <SelectTrigger>
                  <SelectValue placeholder="Select a game" />
                </SelectTrigger>
                <SelectContent>
                  {games.map((game) => (
                    <SelectItem key={game.id} value={game.id}>
                      {game.name}
                    </SelectItem>
                  ))}
                </SelectContent>
              </Select>
            </div>
            {visibleEnvironmentVars.map((variable) => {
              const key = variable.key.trim();
              const label = variable.label?.trim() || key;
              const value = environment[key] ?? String(variable.default ?? '');
              const isSecret =
                key.toLowerCase().includes('password') || key.toLowerCase().includes('license');

              return (
                <div key={key} className="space-y-2">
                  <Label>
                    {label}
                    {variable.required ? ' *' : ''}
                  </Label>
                  {variable.inputType === 'boolean' ? (
                    <div className="flex flex-wrap gap-2">
                      <Button
                        type="button"
                        variant={value === 'true' ? 'default' : 'outline'}
                        onClick={() =>
                          setEnvironment((current) => ({
                            ...current,
                            [key]: 'true',
                          }))
                        }
                      >
                        {variable.enabledLabel ?? 'Enabled'}
                      </Button>
                      <Button
                        type="button"
                        variant={value === 'false' ? 'secondary' : 'outline'}
                        onClick={() =>
                          setEnvironment((current) => ({
                            ...current,
                            [key]: 'false',
                          }))
                        }
                      >
                        {variable.disabledLabel ?? 'Disabled'}
                      </Button>
                    </div>
                  ) : (
                    <Input
                      type={isSecret ? 'password' : 'text'}
                      value={value}
                      onChange={(e) =>
                        setEnvironment((current) => ({
                          ...current,
                          [key]: e.target.value,
                        }))
                      }
                      placeholder={variable.description || label}
                    />
                  )}
                  {variable.description && (
                    <p className="text-xs text-muted-foreground">{variable.description}</p>
                  )}
                </div>
              );
            })}
            <Button
              onClick={() => setStep(2)}
              disabled={!name || !gameId || missingRequiredEnvironment}
            >
              Next
            </Button>
          </CardContent>
        </Card>
      )}

      {step === 2 && (
        <Card>
          <CardHeader>
            <CardTitle>Node & Allocation</CardTitle>
            <CardDescription>Choose where to host your server</CardDescription>
          </CardHeader>
          <CardContent className="space-y-4">
            <div className="space-y-2">
              <Label>Node</Label>
              <Select
                value={nodeId}
                onValueChange={(v) => {
                  setNodeId(v);
                  setAllocationId('');
                  setAdditionalAllocationIds({});
                }}
              >
                <SelectTrigger>
                  <SelectValue placeholder="Select a node" />
                </SelectTrigger>
                <SelectContent>
                  {nodes.map((node) => (
                    <SelectItem key={node.id} value={node.id}>
                      {node.name} ({node.fqdn})
                    </SelectItem>
                  ))}
                </SelectContent>
              </Select>
            </div>
            {nodeId && (
              <div className="space-y-2">
                <Label>Port Allocation</Label>
                <Select
                  value={allocationId}
                  onValueChange={(value) => {
                    setAllocationId(value);
                    setAdditionalAllocationIds((current) =>
                      Object.fromEntries(
                        Object.entries(current).filter(([, selectedId]) => selectedId !== value),
                      ),
                    );
                  }}
                >
                  <SelectTrigger>
                    <SelectValue placeholder="Select a port" />
                  </SelectTrigger>
                  <SelectContent>
                    {freeAllocations.map((a) => (
                      <SelectItem key={a.id} value={a.id}>
                        {a.ip}:{a.port}
                      </SelectItem>
                    ))}
                  </SelectContent>
                </Select>
                {freeAllocations.length === 0 && nodeId && (
                  <p className="text-sm text-destructive">No free allocations on this node</p>
                )}
              </div>
            )}
            {nodeId &&
              additionalPortRequirements.map((requirement) => {
                const options = allocationOptionsForRequirement(requirement.key);
                const protocols = requirement.protocols.join('/').toUpperCase();

                return (
                  <div key={requirement.key} className="space-y-2">
                    <Label>
                      {requirement.label} ({protocols})
                    </Label>
                    <Select
                      value={additionalAllocationIds[requirement.key] ?? ''}
                      onValueChange={(value) =>
                        setAdditionalAllocationIds((current) => ({
                          ...current,
                          [requirement.key]: value,
                        }))
                      }
                    >
                      <SelectTrigger>
                        <SelectValue placeholder={`Select port ${requirement.defaultPort}`} />
                      </SelectTrigger>
                      <SelectContent>
                        {options.map((allocation) => (
                          <SelectItem key={allocation.id} value={allocation.id}>
                            {allocation.ip}:{allocation.port}
                          </SelectItem>
                        ))}
                      </SelectContent>
                    </Select>
                    <p className="text-xs text-muted-foreground">{requirement.description}</p>
                    {options.length === 0 && (
                      <p className="text-sm text-destructive">
                        No free allocation available for this port
                      </p>
                    )}
                  </div>
                );
              })}
            <div className="flex gap-2">
              <Button variant="outline" onClick={() => setStep(1)}>
                Back
              </Button>
              <Button
                onClick={() => setStep(3)}
                disabled={!nodeId || !allocationId || missingRequiredAdditionalPorts}
              >
                Next
              </Button>
            </div>
          </CardContent>
        </Card>
      )}

      {step === 3 && (
        <Card>
          <CardHeader>
            <CardTitle>Resources</CardTitle>
            <CardDescription>Set resource limits for this server</CardDescription>
          </CardHeader>
          <CardContent className="space-y-4">
            <div className="space-y-2">
              <Label>Memory (MB)</Label>
              <Input
                type="number"
                value={memoryLimit}
                onChange={(e) => setMemoryLimit(Number(e.target.value))}
                min={128}
              />
              <p className="text-xs text-muted-foreground">
                {formatBytes(memoryLimit * 1024 * 1024)}
              </p>
            </div>
            <div className="space-y-2">
              <Label>Disk (MB)</Label>
              <Input
                type="number"
                value={diskLimit}
                onChange={(e) => setDiskLimit(Number(e.target.value))}
                min={256}
              />
              <p className="text-xs text-muted-foreground">
                {formatBytes(diskLimit * 1024 * 1024)}
              </p>
            </div>
            <div className="space-y-2">
              <Label>CPU Limit (%)</Label>
              <Input
                type="number"
                value={cpuLimit}
                onChange={(e) => setCpuLimit(Number(e.target.value))}
                min={10}
                max={10000}
              />
              <p className="text-xs text-muted-foreground">100% = 1 core</p>
            </div>
            <div className="flex gap-2">
              <Button variant="outline" onClick={() => setStep(2)}>
                Back
              </Button>
              <Button onClick={handleCreate} disabled={createMutation.isPending}>
                {createMutation.isPending ? 'Creating...' : 'Create Server'}
              </Button>
            </div>
            {createMutation.isError && (
              <p className="text-sm text-destructive">Failed to create server. Please try again.</p>
            )}
          </CardContent>
        </Card>
      )}
    </div>
  );
}
