import type {
  StaffLdb,
  NrccMessage,
  TrainServices,
  ServiceLocation,
} from "@owlboard/ts-types";

import { tz } from "moment-timezone";
import { removeNewlineAndPTag } from "../../newSanitizer";

/// I do not yet have a type defined for any of the input object
export function transform(input: any): StaffLdb | null {
  console.time("StaffLdb Transformation");
  const data = input.GetBoardResult;
  let output: StaffLdb;
  try {
    output = {
      generatedAt: transformDateTime(data?.generatedAt) || new Date(),
      locationName: data?.locationName || 'Not Found',
      stationManagerCode: data?.stationManagerCode || "UK",
      nrccMessages: transformNrcc(data?.nrccMessages) || undefined,
      trainServices: transformTrainServices(data?.trainServices) || undefined,
      busServices: transformTrainServices(data?.busServices) || undefined,
      ferryServices: transformTrainServices(data?.ferryServices) || undefined,
    };
    console.timeEnd("StaffLdb Transformation");
    if (output.locationName !== 'Not Found') {
      return output;
    }
  } catch (err) {
    console.log("utils/translators/ldb/staffLdb.transform: Caught Error");
    console.log("Unable to parse data, assuming no data. ERR: " + err);
  }
  console.timeEnd("StaffLdb Transformation");
  return null;
}

function transformDateTime(input: string): Date {
  //console.log("Transform Date Time Running")
  return new Date(input);
}

function transformNrcc(input: any): NrccMessage[] | undefined {
  //console.log("Transform Nrcc Running")
  if (input === undefined) {
    return input
  }
  let output: NrccMessage[] = [];
  let messages = input;
  if (!Array.isArray(input?.message)) {
    messages = [input?.message];
  }
  if (messages.length) {
    for (const item of messages) {
      let message: NrccMessage = {
        severity: item?.severity,
        xhtmlMessage: removeNewlineAndPTag(item?.xhtmlMessage),
      };
      output.push(message);
    }
    return output;
  }
  return undefined;
}

function transformTrainServices(input: any): TrainServices[] {
  //console.log("Transform Train Services Running")
  let services: any = input?.service;
  let output: TrainServices[] = [];
  if (services === undefined) {
    return output;
  }
  if (!Array.isArray(input.service)) {
    services = [input.service];
  }
  for (const service of services) {
    const times = parseTimes(service)
    const trainService: TrainServices = {
      rid: service?.rid,
      uid: service?.uid,
      trainid: service?.trainid,
      operatorCode: service?.operatorCode || "UK",
      platform: service?.platform || "-",
      platformIsHidden: service?.platformIsHidden,
      serviceIsSupressed: checkIsSupressed(service),
      origin: transformLocation(service?.origin),
      destination: transformLocation(service?.destination),
      length: calculateLength(service),
      isCancelled: service?.isCancelled,
      cancelReason: service?.cancelReason,
      delayReason: service?.delayReason,
      arrivalType: service?.arrivalType,
      departureType: service?.departureType,
      sta: times.sta,
      eta: times.eta,
      ata: times.ata,
      std: times.std,
      etd: times.etd,
      atd: times.atd,
    };
    Object.keys(trainService).forEach(
      (key) => trainService[key] === undefined && delete trainService[key]
    );
    output.push(trainService);
  }
  return output;
}

function checkIsSupressed(service: TrainServices): string | undefined {
  if (service.serviceIsSupressed === "true" || service.isNonPublic === "true") {
    return "true";
  } else {
    return undefined;
  }
}

function transformLocation(input: any): ServiceLocation[] {
  //console.log("Transform Location Running")
  let output: ServiceLocation[] = [];
  let locations: any[] = input.location;
  if (!Array.isArray(input.location)) {
    locations = [input.location];
  }
  for (const item of locations) {
    const location: ServiceLocation = {
      tiploc: item?.tiploc,
    };
    if (item?.via) {
      location.via = item.via;
    }
    output.push(location);
  }
  return output;
}

export function calculateLength(input: any): number | undefined {
  //console.log("Calculate Length Running")
  let length: number;
  if (input?.length) {
    length = input.length;
    return Number(length);
  }
  if (input?.formation?.coaches?.coach) {
    length = input.formation.coaches.coach.length;
    return Number(length);
  }
  return undefined;
}

function transformUnspecifiedDateTime(input: string): Date | undefined {
  //console.log("Transform Unspecified Date Time Running")
  if (!input) {
    return undefined;
  }
  const date = tz(input, "Europe/London");
  return date.toDate();
}

function parseTimes(service: TrainServices) {
  let {sta, eta, ata, std, etd, atd}  = Object.fromEntries(
    Object.entries(service).map(([key, value]) => [key, transformUnspecifiedDateTime(value)])
  )

  let etaResult: Date | undefined | string = eta;
  let ataResult: Date | undefined | string = ata;
  let etdResult: Date | undefined | string = etd;
  let atdResult: Date | undefined | string = atd;
  
  if (sta) {
    if (eta !== undefined && Math.abs(eta.getTime() - sta.getTime()) / 60000 <= 1.5) {
      etaResult = "RT";
    }
    if (ata !== undefined && Math.abs(ata.getTime() - sta.getTime()) / 60000 <= 1.5) {
      ataResult = "RT";
    }
  }
  
  if (std) {
    if (etd !== undefined && Math.abs(etd.getTime() - std.getTime()) / 60000 <= 1.5) {
      etdResult = "RT";
    }
    if (atd !== undefined && Math.abs(atd.getTime() - std.getTime()) / 60000 <= 1.5) {
      atdResult = "RT";
    }
  }

  return {
    sta: sta,
    eta: etaResult,
    ata: ataResult,
    std: std,
    etd: etdResult,
    atd: atdResult,
  }
}
