<script lang="ts">
  import { browser, building, dev } from '$app/environment';
  import { page } from '$app/state';
  import { S } from '$lib';
  import { operators } from './aliases';
  import LineGlyph from './assets/LineGlyph.svelte';
  import Pictogram from './assets/Pictogram.svelte';
  import { Mode, type StoptimesResponse } from './motis-types';
  import { m } from './paraglide/messages';

  let relativeSecondPrecision = $derived(
    ['seconds', 'second', 'sec', 'secs', 's', '2'].includes(
      building ? null! : page.url.searchParams.get('relative')!
    )
  );
  let isRelativeTime = $derived(
    relativeSecondPrecision ||
      ['1', 'true'].includes(
        building ? null! : page.url.searchParams.get('relative')!
      )
  );
  /** only if isRelativeTime is true */
  let now = $state(0);
  const updateNow = () => {
    now = Date.now();
    setTimeout(updateNow, 33);
  };
  $effect(() => (isRelativeTime ? updateNow() : void 0));

  let {
    stopTimes,
    isArrivals = false,
    placeName = '',
    placeId,
    isResultsPage = true,
    setSearch = (q) => void 0,
  }: {
    stopTimes: null | StoptimesResponse;
    isArrivals?: boolean;
    placeName?: string;
    isResultsPage?: boolean;
    placeId: string | null;
    setSearch: (query: string) => void;
  } = $props();
  const timeRelative = (ms: number, relativeTo = now) => {
    const _totalMillis = ms - relativeTo;
    const totalMillis = Math.abs(_totalMillis);
    const totalSeconds = totalMillis / 1000;
    const totalMinutes = totalSeconds / 60;
    const totalHours = totalMinutes / 60;
    const hours = Math.floor(totalHours);
    const minutes = Math.floor(totalMinutes) - hours * 60;
    const seconds = Math.floor(totalSeconds) - hours * 60 * 60 - minutes * 60;
    const results = [] as string[];
    if (hours) results.push(`${hours} ${hours !== 1 ? m.hours() : m.hour()}`);
    if (hours || minutes)
      results.push(`${minutes} ${minutes !== 1 ? m.minutes() : m.minute()}`);
    if (seconds && relativeSecondPrecision)
      results.push(`${seconds} ${seconds !== 1 ? m.seconds() : m.second()}`);
    if (results.length > 1) {
      const [r, l] = [results.pop()!, results.pop()!];
      results.push(
        m.timeJoiner({
          l,
          r,
        })
      );
    }
    const relTime =
      results.length === 1
        ? results[0]
        : results.length === 0
          ? ''
          : results.reduce((pv, cv) => m.timeJoiner2({ l: pv, r: cv }));
    if (relTime === '')
      return m.timeImmediate({
        isPastTense: _totalMillis < 0 ? 'true' : 'false',
      });
    if (_totalMillis < 0)
      return m.timeInPast({
        relTime,
      });
    else
      return m.timeInFuture({
        relTime,
      });
  };
  const localTime = async (
    time: Date,
    forceAbsolute = false,
    relativeTo = now
  ) => {
    const offset = parseFloat(
      browser
        ? // Browsers like Librewolf love patching time to be a fixed timezone, breaking us.
          (localStorage.getItem(
            isRelativeTime && !forceAbsolute
              ? 'relative-offset-time'
              : 'offset-time'
          ) ?? '0')
        : // @ts-ignore
          ((typeof process !== 'undefined' ? process.env : undefined)?.env[
            isRelativeTime && !forceAbsolute
              ? 'PRIVATE_RELATIVE_TIME_OFFSET'
              : 'PRIVATE_SERVER_TIMEZONE'
          ] ?? '0')
    );
    if (isRelativeTime && !forceAbsolute) {
      return timeRelative(time.getTime() - offset * 60 * 60 * 1000, relativeTo);
    } else {
      let hours = time.getHours() + Math.floor(offset);
      const minutes = time.getMinutes() + Math.floor((offset % 1) * 60);
      const seconds =
        time.getSeconds() + Math.floor(((offset % 1) * 60) % 1) * 60;
      if (hours < 0) while (hours < 0) hours += 24;
      else if (hours >= 24) while (hours >= 24) hours -= 24;
      return (
        hours.toString().padStart(2, '0') +
        ':' +
        minutes.toString().padStart(2, '0') +
        (seconds !== 0 ? `:${seconds.toString().padStart(2, '0')}` : '')
      );
    }
  };
</script>

{#snippet renderLocalTime(
  time: Date,
  forceAbsolute = false
)}{#await localTime(time, forceAbsolute, now)}{isRelativeTime && !forceAbsolute
      ? timeRelative(time.getTime(), now)
      : time.getUTCHours() +
        ':' +
        time.getUTCMinutes() +
        (time.getUTCSeconds() !== 0 ? `:${time.getUTCSeconds()}` : '') +
        ' UTC'}{:then t}{t}{/await}{/snippet}

{#if stopTimes}
  {#each stopTimes.stopTimes
    // garbage data
    .filter((v) => v.agencyUrl !== 'http://www.rta.ae') as departure}
    {@const expectedTime =
      new Date(
        (isArrivals
          ? departure.place.scheduledArrival
          : departure.place.scheduledDeparture) ?? '0'
      ).getTime() /
      1000 /
      60}
    {@const receivedTime =
      new Date(
        (isArrivals ? departure.place.arrival : departure.place.departure) ??
          '0'
      ).getTime() /
      1000 /
      60}
    {@const delayMinutes = receivedTime - expectedTime}
    {@const avoidGlyph = departure.routeShortName.startsWith('FlixTrain')}
    {@const routeShortName = (() => {
      let n = departure.routeShortName;
      if (n.startsWith('EC ')) n = n.replace('EC ', 'EC');
      if (departure.mode === 'TRAM' && !isNaN(parseInt(n))) n = 'T ' + n;
      if (departure.mode === 'BUS' && !isNaN(parseInt(n))) n = 'B ' + n;
      if (
        departure.routeShortName === 'European Sleeper' &&
        departure.agencyName === 'Eu Sleeper'
      )
        n = 'EN'; // TODO: validate these are real euronights
      if (n === '?') n = '';
      if (n.startsWith('FlixTrain ')) n = n.substring(10);
      // Note: may also catch ECs/ICs/EXTs operated by DB
      if (
        departure.agencyId === '12681' &&
        departure.agencyName === 'DB Fernverkehr AG' &&
        departure.mode === 'HIGHSPEED_RAIL' &&
        departure.source.startsWith('de_DELFI.gtfs.zip/') &&
        !isNaN(parseInt(n))
      )
        n = `ICE ${n}`;
      return n;
    })()}
    {@const pictogram = (() => {
      switch (true) {
        case departure.mode === Mode.Bike:
          return 'Velo_l';
        case departure.mode === Mode.ODM:
        case departure.mode === Mode.Rental:
          return 'Taxi_l';
        case departure.mode === Mode.Car:
        case departure.mode === Mode.CarDropoff:
        case departure.mode === Mode.CarParking:
          return 'Auto_l';

        // Transit //
        case departure.mode === Mode.Airplane:
          return 'Abflug_l';
        case departure.mode === Mode.LongDistanceRail:
        case departure.mode === Mode.RegionalFastRail:
        case departure.mode === Mode.RegionalRail:
        case departure.mode === Mode.Metro:
        case departure.mode === Mode.HighspeedRail:
          return 'Zug_l';
        case departure.mode === Mode.NightRail:
          return 'Schlafwagen';
        case departure.mode === Mode.Subway:
          return 'Metro_l_' + (m.lang_short() === 'en' ? 'de' : m.lang_short());
        case departure.mode === Mode.Bus:
          return 'Bus_l';
        case departure.mode === Mode.Coach:
          return 'Fernbus_l';
        case departure.mode === Mode.Tram:
        case departure.mode === Mode.CableTram:
          return 'Tram_l';
        case departure.mode === Mode.Funicular:
          return 'Zahnradbahn_l';
        case departure.mode === Mode.AerialLift:
          // return 'Gondelbahn_l';
          return 'Luftseilbahn_l';
        case departure.mode === Mode.Ferry:
          return 'Schiff_l';
        case departure.mode === Mode.Other:
        default:
          return 'Licht';
      }
    })()}
    {@const notices = (() => {
      let notices = [] as [pictogram: string[], content: string][];
      if (departure.cancelled)
        notices.push([
          ['Cancellation', 'Attention'],
          m.brief_north_otter_cherish(),
        ]);
      if (delayMinutes < -0.5) {
        notices.push([
          ['Hint'],
          m.vehicle_is_early({
            minutes: -delayMinutes,
            arrival: isArrivals.toString(),
          }),
        ]);
      } else if (delayMinutes >= 1) {
        notices.push([
          delayMinutes >= 3
            ? ['Delay', 'Attention']
            : delayMinutes >= 2
              ? ['Delay', 'Hint']
              : ['Hint'],
          m.next_long_lark_bask({
            minutes: delayMinutes.toFixed(0),
          }),
        ]);
      }
      return notices;
    })()}
    {@const situationIsBad = notices.find((v) => v[0].includes('Attention'))}
    <div
      class={{
        'p-4 pr-3 sm:pr-4 md:p-6 md:pr-6 rounded-xl': true,
        'bg-[#28282C]': !situationIsBad,
        'bg-[#452525]': situationIsBad,
      }}
      data-data={dev ? JSON.stringify(departure, null, 2) : undefined}
    >
      <div class="flex gap-1 md:items-center md:flex-row flex-col flex-wrap">
        <div class="pictoline flex gap-1 md:items-center flex-r">
          {#if pictogram}
            <Pictogram which={pictogram} />
          {/if}
          {#if ([Mode.NightRail, Mode.HighspeedRail, Mode.LongDistanceRail, Mode.RegionalFastRail, Mode.RegionalRail].includes(departure.mode) || (departure.mode === 'BUS' && routeShortName.startsWith('EV')) || (departure.mode === 'METRO' && departure.routeShortName.startsWith('S'))) && !avoidGlyph}
            <LineGlyph
              currentColor="#fff"
              kind={routeShortName}
              nightIsFilled={false}
            />
          {:else}
            <span
              class="ml-1 -mr-0.5 md:mt-0.5 font-sbb-typo text-nowrap font-bold"
            >
              {routeShortName}
            </span>
          {/if}
          <span class="ml-1 -mr-0.5 md:mt-0.5 font-sbb-typo">
            <!-- {isArrivals ? m.from() : m.to()} -->
            {m.to()}
            <span class="font-semibold">{departure.headsign}</span>
          </span>
        </div>
        <div class="flex-1"></div>
        {#if departure.place.scheduledTrack && departure.place.track}
          <span
            class={{
              'ml-1 mt-0.5 font-[SBB,Inter,system-ui,sans-serif]': true,
              'text-red-400':
                departure.place.scheduledTrack !== departure.place.track,
            }}
          >
            {#if departure.place.name !== placeName}{`${
                departure.place.name === placeName + ', Bahnhof'
                  ? placeName + ', Busbahnhof'
                  : departure.place.name
              }, `}
            {/if}<span class="font-semibold"
              >{m.station_location({
                track: departure.place.track,
                mode: departure.mode,
              })}</span
            >
          </span>
        {:else if departure.place.name !== placeName}{departure.place.name ===
          placeName + ', Bahnhof'
            ? placeName + ', Busbahnhof'
            : departure.place.name}
        {/if}
      </div>
      <div class="flex gap-1 items-center">
        {departure.cancelled
          ? m.antsy_weird_cowfish_wish() + ' '
          : ''}{isRelativeTime && Math.abs(expectedTime - receivedTime) < 1
          ? isArrivals
            ? m.hour_tidy_hawk_explore()
            : m.free_knotty_ray_soar()
          : isArrivals
            ? m.polite_lucky_angelfish_amaze()
            : m.home_flaky_jurgen_ascend()}
        {#if Math.abs(expectedTime - receivedTime) < 1}
          <span class="font-bold">
            {@render renderLocalTime(new Date(receivedTime * 60 * 1000))}
          </span>
        {:else}
          <span class="line-through">
            {@render renderLocalTime(new Date(expectedTime * 60 * 1000), true)}
          </span>
          <span class="font-bold">
            {@render renderLocalTime(new Date(receivedTime * 60 * 1000), true)}
          </span>
          {#if isRelativeTime}
            <span class="opacity">
              ({@render renderLocalTime(new Date(receivedTime * 60 * 1000))})
            </span>
          {/if}
        {/if}
      </div>
      {#if notices.length !== 0}
        <div class="notices pt-2 flex flex-col gap-1">
          {#each notices as notice}
            <div class="flex items-center gap-2">
              {#each notice[0] as pictogram}<Pictogram
                  which={pictogram}
                />{/each}
              <span class="ml-0.5">
                {notice[1]}
              </span>
            </div>
          {/each}
        </div>
      {/if}
      <!-- <pre>{JSON.stringify(departure, null, 2)}</pre> -->
      {#if departure.agencyName}
        <small class="-mb-1 mt-2 block opacity-70"
          >{m.operated_by({
            operator: operators.has(departure.agencyName)
              ? operators.get(departure.agencyName)!
              : departure.agencyName,
          })}{#if departure.agencyName === 'DB Fernverkehr AG'}
            {' '}
            <b>·</b>
            {m.line_number_accuracy()}{/if}</small
        >
      {/if}
    </div>
  {/each}
{:else}
  <div class="flex items-center justify-center">
    <div class="results">
      {#if (placeName || placeId) && isResultsPage}
        <h2 class="text-2xl opacity-90">No results</h2>
        <p>
          No results have been found for the station <b
            >{placeName ?? placeId}</b
          >.<br />
          Please try again.
        </p>
      {:else if placeId}
        <h2 class="text-2xl opacity-90">No results</h2>
        <p>
          No results have been found for the station <b
            >{placeName ?? placeId}</b
          >.<br />
          Please try again.
        </p>
      {:else}
        <h2 class="text-2xl opacity-90">No Station</h2>
        <p class="pb-1">
          Please input a station in the search field above and select a search
          result.
        </p>
        <p class="py-1">
          Examples:
          <span class="pt-2 flex flex-wrap gap-2 max-w-xl">
            <button
              class="{S.button('secondary').replace(
                'not-disabled:bg-[#0000]',
                'not-disabled:bg-[#2E2E3299]'
              )} flex-1 text-nowrap p-3 w-max min-w-20"
              onclick={() => setSearch('Zürich HB')}>Zürich HB</button
            >
            <button
              class="{S.button('secondary').replace(
                'not-disabled:bg-[#0000]',
                'not-disabled:bg-[#2E2E3299]'
              )} flex-1 text-nowrap p-3 w-max min-w-20"
              onclick={() => setSearch('Bielefeld Hbf')}>Bielefeld Hbf</button
            >
            <button
              class="{S.button('secondary').replace(
                'not-disabled:bg-[#0000]',
                'not-disabled:bg-[#2E2E3299]'
              )} flex-1 text-nowrap p-3 w-max min-w-20"
              onclick={() => setSearch('Berlin Hbf')}>Berlin Hbf</button
            >
            <button
              class="{S.button('secondary').replace(
                'not-disabled:bg-[#0000]',
                'not-disabled:bg-[#2E2E3299]'
              )} flex-1 text-nowrap p-3 w-max min-w-20"
              onclick={() => setSearch('Hamburg Hbf')}>Hamburg Hbf</button
            >
            <button
              class="{S.button('secondary').replace(
                'not-disabled:bg-[#0000]',
                'not-disabled:bg-[#2E2E3299]'
              )} flex-1 text-nowrap p-3 w-max min-w-20"
              onclick={() => setSearch('Bern')}>Bern</button
            >
            <button
              class="{S.button('secondary').replace(
                'not-disabled:bg-[#0000]',
                'not-disabled:bg-[#2E2E3299]'
              )} flex-1 text-nowrap p-3 w-max min-w-20"
              onclick={() => setSearch('Basel SBB')}>Basel SBB</button
            >
            <button
              class="{S.button('secondary').replace(
                'not-disabled:bg-[#0000]',
                'not-disabled:bg-[#2E2E3299]'
              )} flex-1 text-nowrap p-3 w-max min-w-20"
              onclick={() => setSearch('Genève-Aéroport')}
              >Genève-Aéroport</button
            >
          </span>
        </p>
      {/if}
    </div>
  </div>
{/if}
