import './styles/global.css';

import type { LatLngExpression } from 'leaflet';
import { useEffect, useMemo, useState } from 'react';

import { LoginForm } from './components/auth/LoginForm';
import { NetworkMap } from './components/map/NetworkMap';
import { useNetworkSnapshot } from './hooks/useNetworkSnapshot';
import { useAuth } from './state/AuthContext';
import type { Station } from './types/domain';
import { buildTrackAdjacency, computeRoute } from './utils/route';

function App(): JSX.Element {
  const { token, user, status: authStatus, logout } = useAuth();
  const isAuthenticated = authStatus === 'authenticated' && token !== null;
  const { data, status, error } = useNetworkSnapshot(isAuthenticated ? token : null);
  const [focusedStationId, setFocusedStationId] = useState<string | null>(null);
  const [routeSelection, setRouteSelection] = useState<{
    startId: string | null;
    endId: string | null;
  }>({ startId: null, endId: null });

  useEffect(() => {
    if (status !== 'success' || !data?.stations.length) {
      setFocusedStationId(null);
      setRouteSelection({ startId: null, endId: null });
      return;
    }

    if (!focusedStationId || !hasStation(data.stations, focusedStationId)) {
      setFocusedStationId(data.stations[0].id);
    }
  }, [status, data, focusedStationId]);

  useEffect(() => {
    if (status !== 'success' || !data) {
      return;
    }

    setRouteSelection((current) => {
      const startExists = current.startId
        ? hasStation(data.stations, current.startId)
        : false;
      const endExists = current.endId
        ? hasStation(data.stations, current.endId)
        : false;

      return {
        startId: startExists ? current.startId : null,
        endId: endExists ? current.endId : null,
      };
    });
  }, [status, data]);

  const stationById = useMemo(() => {
    if (!data) {
      return new Map<string, Station>();
    }
    const lookup = new Map<string, Station>();
    for (const station of data.stations) {
      lookup.set(station.id, station);
    }
    return lookup;
  }, [data]);

  const trackAdjacency = useMemo(
    () => buildTrackAdjacency(data ? data.tracks : []),
    [data]
  );

  const routeComputation = useMemo(
    () =>
      computeRoute({
        startId: routeSelection.startId,
        endId: routeSelection.endId,
        stationById,
        adjacency: trackAdjacency,
      }),
    [routeSelection, stationById, trackAdjacency]
  );

  const routeSegments = useMemo<LatLngExpression[][]>(() => {
    return routeComputation.segments.map((segment) =>
      segment.map((pair) => [pair[0], pair[1]] as LatLngExpression)
    );
  }, [routeComputation.segments]);

  const focusedStation = useMemo(() => {
    if (!data || !focusedStationId) {
      return null;
    }
    return stationById.get(focusedStationId) ?? null;
  }, [data, focusedStationId, stationById]);

  const handleStationSelection = (stationId: string) => {
    setFocusedStationId(stationId);
    setRouteSelection((current) => {
      if (!current.startId || (current.startId && current.endId)) {
        return { startId: stationId, endId: null };
      }

      if (current.startId === stationId) {
        return { startId: stationId, endId: null };
      }

      return { startId: current.startId, endId: stationId };
    });
  };

  const clearRouteSelection = () => {
    setRouteSelection({ startId: null, endId: null });
  };

  return (
    <div className="app-shell">
      <header className="app-header">
        <div>
          <h1>Rail Game</h1>
          <p>Build and manage a railway network using real-world map data.</p>
        </div>
        {isAuthenticated && (
          <div className="auth-meta">
            <span>Signed in as {user?.fullName ?? user?.username}</span>
            <button type="button" onClick={() => logout()} className="ghost-button">
              Sign out
            </button>
          </div>
        )}
      </header>
      <main className="app-main">
        {!isAuthenticated ? (
          <section className="card">
            <LoginForm />
          </section>
        ) : (
          <section className="card">
            <h2>Network Snapshot</h2>
            {status === 'loading' && <p>Loading network data…</p>}
            {status === 'error' && <p className="error-text">{error}</p>}
            {status === 'success' && data && (
              <div className="snapshot-layout">
                <div className="map-wrapper">
                  <NetworkMap
                    snapshot={data}
                    focusedStationId={focusedStationId}
                    startStationId={routeSelection.startId}
                    endStationId={routeSelection.endId}
                    routeSegments={routeSegments}
                    onStationClick={handleStationSelection}
                  />
                </div>
                <div className="route-panel">
                  <div className="route-panel__header">
                    <h3>Route Selection</h3>
                    <button
                      type="button"
                      className="ghost-button"
                      onClick={clearRouteSelection}
                      disabled={!routeSelection.startId && !routeSelection.endId}
                    >
                      Clear
                    </button>
                  </div>
                  <p className="route-panel__hint">
                    Click a station to set the origin, then click another station to
                    preview the rail corridor between them.
                  </p>
                  <dl className="route-panel__meta">
                    <div>
                      <dt>Origin</dt>
                      <dd>
                        {routeSelection.startId
                          ? (stationById.get(routeSelection.startId)?.name ??
                            'Unknown station')
                          : 'Choose a station'}
                      </dd>
                    </div>
                    <div>
                      <dt>Destination</dt>
                      <dd>
                        {routeSelection.endId
                          ? (stationById.get(routeSelection.endId)?.name ??
                            'Unknown station')
                          : 'Choose a station'}
                      </dd>
                    </div>
                    <div>
                      <dt>Estimated Length</dt>
                      <dd>
                        {routeComputation.totalLength !== null
                          ? `${(routeComputation.totalLength / 1000).toFixed(2)} km`
                          : 'N/A'}
                      </dd>
                    </div>
                  </dl>
                  {routeComputation.error && (
                    <p className="route-panel__error">{routeComputation.error}</p>
                  )}
                  {!routeComputation.error && routeComputation.stations && (
                    <div className="route-panel__path">
                      <span>Path:</span>
                      <ol>
                        {routeComputation.stations.map((station) => (
                          <li key={`route-station-${station.id}`}>{station.name}</li>
                        ))}
                      </ol>
                    </div>
                  )}
                </div>
                <div className="grid">
                  <div>
                    <h3>Stations</h3>
                    <ul>
                      {data.stations.map((station) => (
                        <li key={station.id}>
                          <button
                            type="button"
                            className={`station-list-item${
                              station.id === focusedStationId
                                ? ' station-list-item--selected'
                                : ''
                            }${
                              station.id === routeSelection.startId
                                ? ' station-list-item--start'
                                : ''
                            }${
                              station.id === routeSelection.endId
                                ? ' station-list-item--end'
                                : ''
                            }`}
                            aria-pressed={station.id === focusedStationId}
                            onClick={() => handleStationSelection(station.id)}
                          >
                            <span className="station-list-item__name">
                              {station.name}
                            </span>
                            <span className="station-list-item__coords">
                              {station.latitude.toFixed(3)},{' '}
                              {station.longitude.toFixed(3)}
                            </span>
                            {station.id === routeSelection.startId && (
                              <span className="station-list-item__badge">Origin</span>
                            )}
                            {station.id === routeSelection.endId && (
                              <span className="station-list-item__badge">
                                Destination
                              </span>
                            )}
                          </button>
                        </li>
                      ))}
                    </ul>
                  </div>
                  <div>
                    <h3>Trains</h3>
                    <ul>
                      {data.trains.map((train) => (
                        <li key={train.id}>
                          {train.designation} · {train.capacity} capacity ·{' '}
                          {train.maxSpeedKph} km/h
                        </li>
                      ))}
                    </ul>
                  </div>
                  <div>
                    <h3>Tracks</h3>
                    <ul>
                      {data.tracks.map((track) => (
                        <li key={track.id}>
                          {track.startStationId} → {track.endStationId} ·{' '}
                          {track.lengthMeters > 0
                            ? `${(track.lengthMeters / 1000).toFixed(1)} km`
                            : 'N/A'}
                        </li>
                      ))}
                    </ul>
                  </div>
                </div>
                {focusedStation && (
                  <div className="selected-station">
                    <h3>Focused Station</h3>
                    <dl>
                      <div>
                        <dt>Name</dt>
                        <dd>{focusedStation.name}</dd>
                      </div>
                      <div>
                        <dt>Coordinates</dt>
                        <dd>
                          {focusedStation.latitude.toFixed(5)},{' '}
                          {focusedStation.longitude.toFixed(5)}
                        </dd>
                      </div>
                      {focusedStation.code && (
                        <div>
                          <dt>Code</dt>
                          <dd>{focusedStation.code}</dd>
                        </div>
                      )}
                      {typeof focusedStation.elevationM === 'number' && (
                        <div>
                          <dt>Elevation</dt>
                          <dd>{focusedStation.elevationM.toFixed(1)} m</dd>
                        </div>
                      )}
                      {focusedStation.osmId && (
                        <div>
                          <dt>OSM ID</dt>
                          <dd>{focusedStation.osmId}</dd>
                        </div>
                      )}
                      <div>
                        <dt>Status</dt>
                        <dd>
                          {(focusedStation.isActive ?? true) ? 'Active' : 'Inactive'}
                        </dd>
                      </div>
                    </dl>
                  </div>
                )}
              </div>
            )}
          </section>
        )}
      </main>
    </div>
  );
}

export default App;

function hasStation(stations: Station[], id: string): boolean {
  return stations.some((station) => station.id === id);
}
