import { FormEvent, useMemo, useState } from 'react';
import { Edit, Eye, Plus, Search, Trash2 } from 'lucide-react';

import { Button } from '@/components/ui/button';
import {
  Dialog,
  DialogContent,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from '@/components/ui/dialog';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
import { useCreateRoute, useDeactivateRoute, useRouteYards, useRoutes, useUpdateRoute } from '@/hooks/useRoutes';
import { useToast } from '@/hooks/use-toast';
import type { RouteRecord, YardRef } from '@/services/routes.service';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@edr/ui-common';

type RouteFormState = {
  name: string;
  milestones: string[];
};

const emptyForm = (): RouteFormState => ({ name: '', milestones: ['', ''] });

const yardLabel = (yard?: YardRef | null) => (yard ? `${yard.label} (${yard.code})` : '-');

const routeStops = (route: RouteRecord) =>
  (route.milestones ?? [])
    .sort((left, right) => left.sequenceNo - right.sequenceNo)
    .map((milestone) => milestone.yard?.label ?? milestone.yard?.code ?? milestone.yardId);

const normalizeRouteError = (error: unknown) => {
  const responseData = (error as { response?: { data?: unknown } })?.response?.data;
  const data = responseData && typeof responseData === 'object' ? (responseData as Record<string, unknown>) : undefined;
  const rawMessage = data?.message ?? data?.error ?? (error as Error)?.message;

  return Array.isArray(rawMessage)
    ? rawMessage.join(', ')
    : rawMessage
      ? String(rawMessage)
      : 'Save failed';
};

export default function RoutesPage() {
  const [search, setSearch] = useState('');
  const [formOpen, setFormOpen] = useState(false);
  const [viewing, setViewing] = useState<RouteRecord | null>(null);
  const [editing, setEditing] = useState<RouteRecord | null>(null);
  const [form, setForm] = useState<RouteFormState>(emptyForm());
  const { toast } = useToast();

  const routesQuery = useRoutes();
  const yardsQuery = useRouteYards();
  const createMutation = useCreateRoute();
  const updateMutation = useUpdateRoute();
  const deactivateMutation = useDeactivateRoute();

  const filteredRoutes = useMemo(() => {
    const query = search.trim().toLowerCase();
    if (!query) return routesQuery.data ?? [];

    return (routesQuery.data ?? []).filter((route) => {
      const searchable = [
        route.name,
        route.originYard?.label,
        route.originYard?.code,
        route.destinationYard?.label,
        route.destinationYard?.code,
        ...routeStops(route),
      ]
        .filter(Boolean)
        .join(' ')
        .toLowerCase();

      return searchable.includes(query);
    });
  }, [routesQuery.data, search]);

  const yardOptions = useMemo(
    () =>
      (yardsQuery.data ?? []).map((yard) => ({
        value: yard.id,
        label: `${yard.label} (${yard.code})`,
      })),
    [yardsQuery.data],
  );

  const resetForm = () => {
    setFormOpen(false);
    setEditing(null);
    setForm(emptyForm());
  };

  const openCreate = () => {
    setEditing(null);
    setForm(emptyForm());
    setFormOpen(true);
  };

  const openEdit = (route: RouteRecord) => {
    setEditing(route);
    setForm({
      name: route.name,
      milestones: (route.milestones ?? [])
        .sort((left, right) => left.sequenceNo - right.sequenceNo)
        .map((milestone) => milestone.yardId),
    });
    setFormOpen(true);
  };

  const setMilestone = (index: number, yardId: string) => {
    setForm((current) => ({
      ...current,
      milestones: current.milestones.map((value, currentIndex) =>
        currentIndex === index ? yardId : value,
      ),
    }));
  };

  const addMilestone = () => {
    setForm((current) => ({ ...current, milestones: [...current.milestones, ''] }));
  };

  const removeMilestone = (index: number) => {
    setForm((current) => ({
      ...current,
      milestones: current.milestones.filter((_, currentIndex) => currentIndex !== index),
    }));
  };

  const handleSubmit = async (event: FormEvent) => {
    event.preventDefault();

    if (!form.name.trim()) {
      toast({ title: 'Save failed', description: 'Route name is required', variant: 'destructive' });
      return;
    }

    if (form.milestones.length < 2 || form.milestones.some((yardId) => !yardId)) {
      toast({
        title: 'Save failed',
        description: 'Select at least an origin and destination yard',
        variant: 'destructive',
      });
      return;
    }

    try {
      const payload = {
        name: form.name.trim(),
        milestones: form.milestones.map((yardId) => ({ yardId })),
        isActive: editing?.isActive ?? true,
      };

      if (editing) {
        await updateMutation.mutateAsync({ id: editing.id, data: payload });
        toast({ title: 'Route updated' });
      } else {
        await createMutation.mutateAsync(payload);
        toast({ title: 'Route created' });
      }

      resetForm();
    } catch (error) {
      toast({ title: 'Save failed', description: normalizeRouteError(error), variant: 'destructive' });
    }
  };

  const handleDeactivate = async (route: RouteRecord) => {
    if (!window.confirm('Deactivate this route?')) return;

    try {
      await deactivateMutation.mutateAsync(route.id);
      toast({ title: 'Route deactivated' });
    } catch {
      toast({ title: 'Deactivate failed', description: 'Could not deactivate route', variant: 'destructive' });
    }
  };

  const isSaving = createMutation.isPending || updateMutation.isPending;

  const availableOptionsForIndex = (index: number) => {
    const selectedByOthers = new Set(
      form.milestones.filter((value, currentIndex) => currentIndex !== index && value),
    );

    return yardOptions.filter(
      (option) => option.value === form.milestones[index] || !selectedByOthers.has(option.value),
    );
  };

  return (
    <div className="space-y-5 p-6">
      <div className="flex flex-col gap-4 sm:flex-row sm:items-end sm:justify-between">
        <div>
          <h1 className="text-2xl font-semibold tracking-tight">Routes</h1>
          <p className="mt-1 text-sm text-muted-foreground">
            Build train routes from an ordered yard list where the first stop is the origin and the last stop is the destination.
          </p>
        </div>
        <Button onClick={openCreate}>
          <Plus className="size-4" />
          Add Route
        </Button>
      </div>

      <div className="flex max-w-md items-center gap-2 rounded-md border bg-background px-3">
        <Search className="size-4 text-muted-foreground" />
        <Input
          className="border-0 px-0 shadow-none focus-visible:ring-0"
          placeholder="Search routes"
          value={search}
          onChange={(event) => setSearch(event.target.value)}
        />
      </div>

      <div className="overflow-hidden rounded-lg border bg-card">
        <Table>
          <TableHeader>
            <TableRow>
              <TableHead>Name</TableHead>
              <TableHead>Origin</TableHead>
              <TableHead>Destination</TableHead>
              <TableHead>Milestones</TableHead>
              <TableHead>Status</TableHead>
              <TableHead className="w-[150px] text-right">Actions</TableHead>
            </TableRow>
          </TableHeader>
          <TableBody>
            {filteredRoutes.map((route) => (
              <TableRow key={route.id}>
                <TableCell>{route.name}</TableCell>
                <TableCell>{yardLabel(route.originYard)}</TableCell>
                <TableCell>{yardLabel(route.destinationYard)}</TableCell>
                <TableCell>{Math.max((route.milestones?.length ?? 0) - 2, 0)}</TableCell>
                <TableCell>{route.isActive ? 'Active' : 'Inactive'}</TableCell>
                <TableCell>
                  <div className="flex justify-end gap-1">
                    <Button variant="ghost" size="icon" onClick={() => setViewing(route)} title="View">
                      <Eye className="size-4" />
                    </Button>
                    <Button variant="ghost" size="icon" onClick={() => openEdit(route)} title="Edit">
                      <Edit className="size-4" />
                    </Button>
                    <Button
                      variant="ghost"
                      size="icon"
                      onClick={() => handleDeactivate(route)}
                      title="Deactivate"
                      disabled={!route.isActive || deactivateMutation.isPending}
                    >
                      <Trash2 className="size-4" />
                    </Button>
                  </div>
                </TableCell>
              </TableRow>
            ))}
            {!routesQuery.isLoading && filteredRoutes.length === 0 ? (
              <TableRow>
                <TableCell colSpan={6} className="h-28 text-center text-muted-foreground">
                  No routes found.
                </TableCell>
              </TableRow>
            ) : null}
            {routesQuery.isLoading ? (
              <TableRow>
                <TableCell colSpan={6} className="h-28 text-center text-muted-foreground">
                  Loading...
                </TableCell>
              </TableRow>
            ) : null}
          </TableBody>
        </Table>
      </div>

      <Dialog open={formOpen} onOpenChange={(open) => (!open ? resetForm() : setFormOpen(true))}>
        <DialogContent className="max-h-[90vh] overflow-y-auto">
          <DialogHeader>
            <DialogTitle>{editing ? 'Edit Route' : 'Add Route'}</DialogTitle>
          </DialogHeader>
          <form className="space-y-4" onSubmit={handleSubmit}>
            <div className="space-y-2">
              <Label htmlFor="route-name">Name</Label>
              <Input
                id="route-name"
                value={form.name}
                onChange={(event) => setForm((current) => ({ ...current, name: event.target.value }))}
              />
            </div>

            <div className="space-y-3">
              <div className="flex items-center justify-between">
                <Label>Stops</Label>
                <Button type="button" variant="outline" size="sm" onClick={addMilestone}>
                  <Plus className="size-4" />
                  Add next milestone
                </Button>
              </div>
              {form.milestones.map((yardId, index) => {
                const role = index === 0 ? 'Origin' : index === form.milestones.length - 1 ? 'Destination' : 'Milestone';
                const availableOptions = availableOptionsForIndex(index);
                return (
                  <div key={`${role}-${index}`} className="grid gap-2 rounded-lg border p-3 sm:grid-cols-[120px,1fr,auto] sm:items-center">
                    <p className="text-sm font-medium">{role}</p>
                    <Select value={yardId} onValueChange={(value) => setMilestone(index, value)}>
                      <SelectTrigger>
                        <SelectValue placeholder="Select yard" />
                      </SelectTrigger>
                      <SelectContent>
                        {availableOptions.map((option) => (
                          <SelectItem key={option.value} value={option.value}>
                            {option.label}
                          </SelectItem>
                        ))}
                      </SelectContent>
                    </Select>
                    <Button
                      type="button"
                      variant="ghost"
                      size="icon"
                      onClick={() => removeMilestone(index)}
                      disabled={form.milestones.length <= 2}
                      title="Remove stop"
                    >
                      <Trash2 className="size-4" />
                    </Button>
                  </div>
                );
              })}
            </div>

            <DialogFooter>
              <Button type="button" variant="outline" onClick={resetForm}>
                Cancel
              </Button>
              <Button type="submit" disabled={isSaving}>
                Save
              </Button>
            </DialogFooter>
          </form>
        </DialogContent>
      </Dialog>

      <Dialog open={Boolean(viewing)} onOpenChange={(open) => (!open ? setViewing(null) : null)}>
        <DialogContent>
          <DialogHeader>
            <DialogTitle>Route details</DialogTitle>
          </DialogHeader>
          {viewing ? (
            <div className="space-y-3 text-sm">
              <div>
                <p className="font-medium">Name</p>
                <p className="text-muted-foreground">{viewing.name}</p>
              </div>
              <div>
                <p className="font-medium">Status</p>
                <p className="text-muted-foreground">{viewing.isActive ? 'Active' : 'Inactive'}</p>
              </div>
              <div>
                <p className="font-medium">Stops</p>
                <div className="mt-2 space-y-2">
                  {routeStops(viewing).map((stop, index, stops) => (
                    <div key={`${stop}-${index}`} className="rounded-md border px-3 py-2 text-muted-foreground">
                      {index === 0 ? 'Origin' : index === stops.length - 1 ? 'Destination' : `Milestone ${index}`}:
                      {' '}
                      {stop}
                    </div>
                  ))}
                </div>
              </div>
            </div>
          ) : null}
        </DialogContent>
      </Dialog>
    </div>
  );
}
