import {
  CondensedTimeline,
  CondensedTimelineEntry,
  lastUpdated,
  timelineString,
} from "./data/timeline.js";
import { compareVersions } from "./utils.js";
import {
  BrowserVersion,
  Options,
  AllVersionsOptions,
  AllBrowsersBrowserVersion,
  NestedBrowserVersions,
  TimelineOptions,
  TimelineEvent,
  BrowserTimeline,
} from "./types.js";

const nameMappings: {
  [shortName: string]: { longName: string; engine?: string };
} = {
  c: { longName: "chrome" },
  ca: { longName: "chrome_android" },
  e: { longName: "edge" },
  f: { longName: "firefox" },
  fa: { longName: "firefox_android" },
  s: { longName: "safari" },
  si: { longName: "safari_ios" },
  o: { longName: "opera", engine: "Blink" },
  oa: { longName: "opera_android", engine: "Blink" },
  sa: { longName: "samsunginternet_android", engine: "Blink" },
  wva: { longName: "webview_android", engine: "Blink" },
  y: { longName: "ya_android", engine: "Blink" },
  u: { longName: "uc_android", engine: "Blink" },
  q: { longName: "qq_android", engine: "Blink" },
  k: { longName: "kai_os", engine: "Gecko" },
  fb: { longName: "facebook_android", engine: "Blink" },
  ia: { longName: "instagram_android", engine: "Blink" },
};

const expandReleaseDate = (date: string): string => {
  // If it's a 6-digit date YYMMDD (like 240512), expand to 20YY-MM-DD
  if (date.length === 6 && /^\d{6}$/.test(date)) {
    return `20${date.slice(0, 2)}-${date.slice(2, 4)}-${date.slice(4, 6)}`;
  }
  // If it's an 8-digit date YYYYMMDD (like 19991012), expand to YYYY-MM-DD
  if (date.length === 8 && /^\d{8}$/.test(date)) {
    return `${date.slice(0, 4)}-${date.slice(4, 6)}-${date.slice(6, 8)}`;
  }
  return date;
};

const timeline: CondensedTimeline = {};
const allReleases: Record<string, CondensedTimelineEntry[]> = {};

let changeDate = "";
let parsingReleases = false;

timelineString.split("\n").forEach((line) => {
  line = line.trim();
  if (!line) {
    return;
  }
  if (line === "releases") {
    parsingReleases = true;
    return;
  }
  if (parsingReleases) {
    const parts = line.split(",");
    if (parts.length >= 3) {
      const [shortName, version, releaseDate, engineVersion] = parts;
      const entry: CondensedTimelineEntry = [
        shortName! as any,
        version!,
        expandReleaseDate(releaseDate!.trim()),
      ];
      if (engineVersion) {
        entry.push(engineVersion.trim());
      }
      if (!allReleases[shortName!]) {
        allReleases[shortName!] = [];
      }
      allReleases[shortName!]!.push(entry);
    }
    return;
  }
  if (line.startsWith("20") || line.startsWith("pre_baseline")) {
    changeDate = expandReleaseDate(line);
    timeline[changeDate] = [];
    return;
  } else {
    const parts = line.split(",");
    if (parts.length >= 3) {
      const [shortName, version, releaseDate, engineVersion] = parts;
      const entry: CondensedTimelineEntry = [
        shortName! as any,
        version!,
        expandReleaseDate(releaseDate!.trim()),
      ];
      if (engineVersion) {
        entry.push(engineVersion.trim());
      }
      if (!timeline[changeDate]) {
        throw new Error(
          `Timeline entry for date ${changeDate} is undefined. This should not happen.`,
        );
      }
      timeline[changeDate]!.push(entry);
    }
  }
});

const mergedReleases: Record<string, CondensedTimelineEntry[]> = {};

// Reconstruct the full list of releases for each browser by merging the timeline and allReleases.
Object.keys(nameMappings).forEach((shortName) => {
  mergedReleases[shortName] = [...(allReleases[shortName] ?? [])];
});

Object.keys(timeline).forEach((changeDate) => {
  const changeList = timeline[changeDate]!;
  changeList.forEach((change) => {
    const shortName = change[0];
    if (mergedReleases[shortName]) {
      if (!mergedReleases[shortName].some((item) => item[1] === change[1])) {
        mergedReleases[shortName].push(change);
      }
    }
  });
});

Object.keys(mergedReleases).forEach((shortName) => {
  const list = mergedReleases[shortName]!;
  list.sort((a, b) => compareVersions(a[1], b[1]));
});

let hasWarned = false;

export function _resetHasWarned() {
  hasWarned = false;
}

const checkUpdate = (targetDate: Date, lastUpdatedOverride?: number) => {
  if (
    hasWarned ||
    (typeof process !== "undefined" &&
      process.env &&
      (process.env.BROWSERSLIST_IGNORE_OLD_DATA ||
        process.env.BASELINE_BROWSER_MAPPING_IGNORE_OLD_DATA))
  ) {
    return;
  }

  const twoMonthsAgo = new Date();
  twoMonthsAgo.setMonth(twoMonthsAgo.getMonth() - 2);

  const lastUpdatedToUse = lastUpdatedOverride ?? lastUpdated;

  if (targetDate > twoMonthsAgo && lastUpdatedToUse < twoMonthsAgo.getTime()) {
    console.warn(
      "[baseline-browser-mapping] The data in this module is over two months old and you are targetting a recent feature cut off date of " +
        targetDate.toISOString().slice(0, 10) +
        ". To ensure accurate Baseline data, please update to the latest version of this module using the package manager of your choice." +
        "You can suppress these warnings using the environment variables `BROWSERSLIST_IGNORE_OLD_DATA=true` or `BASELINE_BROWSER_MAPPING_IGNORE_OLD_DATA=true` or by passing `suppressWarnings: true` when you call `getCompatibleVersions()` or `getAllVersions()`.",
    );
    hasWarned = true;
  }
};

// nameMappings is now declared at the top of the file

const coreBrowsers = Object.keys(nameMappings)
  .filter((shortName) => nameMappings[shortName]!.engine === undefined)
  .map((shortName) => {
    const { longName } = nameMappings[shortName]!;
    return { shortName: shortName, longName: longName };
  });

const downstreamOrder: string[] = [
  "webview_android",
  "samsunginternet_android",
  "opera_android",
  "opera",
  "ya_android",
  "uc_android",
  "qq_android",
  "kai_os",
  "facebook_android",
  "instagram_android",
];

const sortBrowserVersions = (versions: BrowserVersion[]): BrowserVersion[] => {
  const coreResults: BrowserVersion[] = [];
  const downstreamResults: BrowserVersion[] = [];

  versions.forEach((item) => {
    const isCore = coreBrowsers.some((b) => b.longName === item.browser);
    if (isCore) {
      coreResults.push(item);
    } else {
      downstreamResults.push(item);
    }
  });

  coreResults.sort((a, b) => {
    if (a.browser < b.browser) return -1;
    if (a.browser > b.browser) return 1;
    return compareVersions(a.version, b.version);
  });

  downstreamResults.sort((a, b) => {
    const indexA = downstreamOrder.indexOf(a.browser);
    const indexB = downstreamOrder.indexOf(b.browser);
    if (indexA !== indexB) {
      return indexA - indexB;
    }
    return compareVersions(a.version, b.version);
  });

  return [...coreResults, ...downstreamResults];
};

type versionsObject = {
  [browser: string]: BrowserVersion;
};

type YearVersions = {
  [year: string]: versionsObject;
};

let suppressPre2015Warning: boolean = false;

const kaiOSWarning = (
  // options: Options | AllVersionsOptions
  options: Options,
) => {
  if (
    options.includeDownstreamBrowsers === false &&
    options.includeKaiOS === true
  ) {
    throw new Error(
      "KaiOS is a downstream browser and can only be included if you include other downstream browsers. Please ensure you use `includeDownstreamBrowsers: true`.",
    );
  }
};

const reconstructBrowserVersion = (
  shortName: string,
  longName: string,
  version: string,
  releaseDate: string,
  engineVersion?: string,
) => {
  const browserVersion: BrowserVersion = {
    browser: longName,
    version: version,
    release_date: releaseDate === "u" ? "unknown" : releaseDate,
  };
  if (engineVersion) {
    browserVersion.engine_version = engineVersion;
    browserVersion.engine = nameMappings[shortName]?.engine;
  }
  return browserVersion;
};

/**
 * Returns browser versions compatible with specified Baseline targets.
 * Defaults to returning the minimum versions of the core browser set that support Baseline Widely available.
 * Takes an optional configuration `Object` with four optional properties:
 * - `listAllCompatibleVersions`: `false` (default) or `true`
 * - `includeDownstreamBrowsers`: `false` (default) or `true`
 * - `widelyAvailableOnDate`: date in format `YYYY-MM-DD`
 * - `targetYear`: year in format `YYYY`
 * - `supressWarnings`: `false` (default) or `true`
 */
export function getCompatibleVersions(userOptions?: Options): BrowserVersion[] {
  let incomingOptions = userOptions ?? {};

  let options: Options = {
    listAllCompatibleVersions:
      incomingOptions.listAllCompatibleVersions ?? false,
    includeDownstreamBrowsers:
      incomingOptions.includeDownstreamBrowsers ?? false,
    widelyAvailableOnDate: incomingOptions.widelyAvailableOnDate ?? undefined,
    targetYear: incomingOptions.targetYear ?? undefined,
    includeKaiOS: incomingOptions.includeKaiOS ?? false,
    overrideLastUpdated: incomingOptions.overrideLastUpdated ?? undefined,
    suppressWarnings: incomingOptions.suppressWarnings ?? false,
  };

  let targetDate: Date = new Date();

  if (!options.widelyAvailableOnDate && !options.targetYear) {
    targetDate = new Date();
  } else if (options.targetYear && options.widelyAvailableOnDate) {
    throw new Error(
      "You cannot use targetYear and widelyAvailableOnDate at the same time.  Please remove one of these options and try again.",
    );
  } else if (options.widelyAvailableOnDate) {
    targetDate = new Date(options.widelyAvailableOnDate);
  } else if (options.targetYear) {
    targetDate = new Date(`${options.targetYear}-12-31`);
  }

  // Sets a cutoff date for feature interoperability 30 months before the stated date
  if (options.widelyAvailableOnDate || options.targetYear === undefined) {
    targetDate.setMonth(targetDate.getMonth() - 30);
  }

  if (!options.suppressWarnings) {
    checkUpdate(targetDate, options.overrideLastUpdated);
    kaiOSWarning(options);
    if (targetDate.getFullYear() < 2015 && !suppressPre2015Warning) {
      console.warn(
        new Error(
          "There are no browser versions compatible with Baseline before 2015.  You may receive unexpected results.",
        ),
      );
    }

    if (targetDate.getFullYear() < 2002) {
      throw new Error(
        "None of the browsers in the core set were released before 2002.  Please use a date after 2002.",
      );
    }

    if (targetDate.getFullYear() > new Date().getFullYear()) {
      throw new Error(
        "There are no browser versions compatible with Baseline in the future",
      );
    }
  }

  const isPre2015 = targetDate < new Date("2015-07-29");

  // Find the active minimum version of each browser at targetDate.
  const minVersions: Record<string, CondensedTimelineEntry | undefined> = {};
  Object.keys(nameMappings).forEach((shortName) => {
    minVersions[shortName] = undefined;
  });

  Object.keys(timeline).forEach((changeDate) => {
    const changeList = timeline[changeDate]!;
    let isBeforeOrAt = false;
    if (changeDate === "pre_baseline") {
      isBeforeOrAt = true;
    } else {
      isBeforeOrAt = new Date(changeDate) <= targetDate;
    }

    if (isBeforeOrAt) {
      changeList.forEach((change) => {
        const shortName = change[0];
        if (changeDate !== "pre_baseline") {
          minVersions[shortName] = change;
        }
      });
    }
  });

  const result: BrowserVersion[] = [];

  Object.keys(nameMappings).forEach((shortName) => {
    const { longName } = nameMappings[shortName]!;
    if (!options.includeKaiOS && shortName === "k") {
      return;
    }
    const isCore = coreBrowsers.some((b) => b.shortName === shortName);
    if (!options.includeDownstreamBrowsers && !isCore) {
      return;
    }

    if (isPre2015) {
      if (!options.listAllCompatibleVersions) {
        result.push({
          browser: longName,
          version: "0",
          release_date: "",
        });
      } else {
        result.push({
          browser: longName,
          version: "0",
          release_date: "",
        });
        const releases = mergedReleases[shortName] ?? [];
        releases.forEach((release) => {
          result.push(
            reconstructBrowserVersion(
              shortName,
              longName,
              release[1],
              release[2],
              release[3],
            ),
          );
        });
      }
    } else {
      const minVersionEntry = minVersions[shortName];
      if (!minVersionEntry) {
        return;
      }

      const minVersion = minVersionEntry[1];

      if (!options.listAllCompatibleVersions) {
        result.push(
          reconstructBrowserVersion(
            shortName,
            longName,
            minVersion,
            minVersionEntry[2],
            minVersionEntry[3],
          ),
        );
      } else {
        const releases = mergedReleases[shortName] ?? [];
        releases.forEach((release) => {
          const version = release[1];
          if (compareVersions(version, minVersion) >= 0) {
            result.push(
              reconstructBrowserVersion(
                shortName,
                longName,
                version,
                release[2],
                release[3],
              ),
            );
          }
        });
      }
    }
  });

  return sortBrowserVersions(result);
}

/**
 * Returns all browser versions known to this module with their level of Baseline support as a JavaScript `Array` (`"array"`), `Object` (`"object"`) or a CSV string (`"csv"`).
 * Takes an optional configuration `Object` with three optional properties:
 * - `includeDownstreamBrowsers`: `false` (default) or `true`
 * - `outputFormat`: `"array"` (default), `"object"` or `"csv"`
 * - `useSupports`: `false` (default) or `true`, replaces `wa_compatible` property with optional `supports` property which returns `widely` or `newly` available when present.
 * - `supressWarnings`: `false` (default) or `true`
 */
export function getAllVersions(
  userOptions?: AllVersionsOptions,
): AllBrowsersBrowserVersion[] | NestedBrowserVersions | string {
  suppressPre2015Warning = true;

  let incomingOptions = userOptions ?? {};

  let options: AllVersionsOptions = {
    outputFormat: incomingOptions.outputFormat ?? "array",
    includeDownstreamBrowsers:
      incomingOptions.includeDownstreamBrowsers ?? false,
    useSupports: incomingOptions.useSupports ?? false,
    includeKaiOS: incomingOptions.includeKaiOS ?? false,
    suppressWarnings: incomingOptions.suppressWarnings ?? false,
  };

  kaiOSWarning(options);

  let nextYear = new Date().getFullYear() + 1;

  const yearArray = [...Array(nextYear).keys()].slice(2015);
  const yearMinimumVersions: YearVersions = {};
  yearArray.forEach((year: number) => {
    yearMinimumVersions[year] = {};
    getCompatibleVersions({
      targetYear: year,
      suppressWarnings: options.suppressWarnings,
    }).forEach((version) => {
      if (yearMinimumVersions[year])
        yearMinimumVersions[year][version.browser] = version;
    });
  });

  const waMinimumVersions = getCompatibleVersions({
    suppressWarnings: options.suppressWarnings,
  });
  const waObject: versionsObject = {};
  waMinimumVersions.forEach((version: BrowserVersion) => {
    waObject[version.browser] = version;
  });

  const thirtyMonthsFromToday = new Date();
  thirtyMonthsFromToday.setMonth(thirtyMonthsFromToday.getMonth() + 30);
  const naMinimumVersions = getCompatibleVersions({
    widelyAvailableOnDate: thirtyMonthsFromToday.toISOString().slice(0, 10),
    suppressWarnings: options.suppressWarnings,
  });

  const naObject: versionsObject = {};
  naMinimumVersions.forEach((version: BrowserVersion) => {
    naObject[version.browser] = version;
  });

  const allVersions = getCompatibleVersions({
    targetYear: 2002,
    listAllCompatibleVersions: true,
    suppressWarnings: options.suppressWarnings,
  });

  const outputArray: AllBrowsersBrowserVersion[] = [];

  coreBrowsers
    .map((browser) => browser.longName)
    .forEach((browserName) => {
      let thisBrowserAllVersions = allVersions
        .filter((version) => version.browser == browserName)
        .sort((a, b) => {
          return compareVersions(a.version, b.version);
        });

      let waVersion = waObject[browserName]?.version ?? "0";
      let naVersion = naObject[browserName]?.version ?? "0";

      yearArray.forEach((year) => {
        if (yearMinimumVersions[year]) {
          let minBrowserVersionInfo = yearMinimumVersions[year][
            browserName
          ] ?? {
            version: "0",
          };
          let minBrowserVersion = minBrowserVersionInfo.version;
          let sliceIndex = thisBrowserAllVersions.findIndex(
            (element) =>
              compareVersions(element.version, minBrowserVersion) === 0,
          );

          let subArray =
            year === nextYear - 1
              ? thisBrowserAllVersions
              : thisBrowserAllVersions.slice(0, sliceIndex);

          subArray.forEach((version) => {
            let isWaCompatible =
              compareVersions(version.version, waVersion) >= 0;
            let isNaCompatible =
              compareVersions(version.version, naVersion) >= 0;

            let versionToPush: AllBrowsersBrowserVersion = {
              ...version,
              year: year <= 2015 ? "pre_baseline" : year - 1,
            };

            if (options.useSupports) {
              if (isWaCompatible) versionToPush.supports = "widely";
              if (isNaCompatible) versionToPush.supports = "newly";
            } else {
              versionToPush = {
                ...versionToPush,
                wa_compatible: isWaCompatible,
              };
            }

            outputArray.push(versionToPush);
          });

          thisBrowserAllVersions = thisBrowserAllVersions.slice(
            sliceIndex,
            thisBrowserAllVersions.length,
          );
        }
      });
    });

  if (options.includeDownstreamBrowsers) {
    let downstreamBrowsers = getCompatibleVersions({
      targetYear: 2002,
      listAllCompatibleVersions: true,
      includeDownstreamBrowsers: true,
      includeKaiOS: options.includeKaiOS,
      suppressWarnings: options.suppressWarnings,
    }).filter(
      (version) =>
        !coreBrowsers.map((b) => b.longName).includes(version.browser),
    );

    downstreamBrowsers.forEach((version: BrowserVersion) => {
      const targetUpstreamBrowser =
        version.engine === "Gecko" ? "firefox" : "chrome";
      let correspondingUpstreamVersion = outputArray.find(
        (upstreamVersion) =>
          upstreamVersion.browser === targetUpstreamBrowser &&
          upstreamVersion.version === version.engine_version,
      );
      if (correspondingUpstreamVersion) {
        if (options.useSupports) {
          outputArray.push({
            ...version,
            year: correspondingUpstreamVersion.year,
            supports: correspondingUpstreamVersion.supports,
          });
        } else {
          outputArray.push({
            ...version,
            year: correspondingUpstreamVersion.year,
            wa_compatible: correspondingUpstreamVersion.wa_compatible,
          });
        }
      }
    });
  }

  outputArray.sort((a, b) => {
    // Sort by year: "pre_baseline" first, then numerical year in ascending order
    if (a.year === "pre_baseline" && b.year !== "pre_baseline") {
      return -1;
    }
    if (b.year === "pre_baseline" && a.year !== "pre_baseline") {
      return 1;
    }
    if (a.year !== "pre_baseline" && b.year !== "pre_baseline") {
      if (a.year < b.year) {
        return -1;
      }
      if (a.year > b.year) {
        return 1;
      }
    }

    // Sort by browser alphabetically
    if (a.browser < b.browser) {
      return -1;
    }
    if (a.browser > b.browser) {
      return 1;
    }

    // Sort by version using compareVersions
    return compareVersions(a.version, b.version);
  });

  if (options.outputFormat === "object") {
    const outputObject: NestedBrowserVersions = {};

    outputArray.forEach((version: AllBrowsersBrowserVersion) => {
      if (!outputObject[version.browser]) {
        outputObject[version.browser] = {};
      }
      let versionToAdd = {
        year: version.year,
        release_date: version.release_date,
        engine: version.engine,
        engine_version: version.engine_version,
      };

      if (options.useSupports) {
        //@ts-ignore
        outputObject[version.browser][version.version] = version.supports
          ? { ...versionToAdd, supports: version.supports }
          : versionToAdd;
      } else {
        //@ts-ignores
        outputObject[version.browser][version.version] = {
          ...versionToAdd,
          wa_compatible: version.wa_compatible,
        };
      }
    });

    return outputObject ?? {};
  }

  if (options.outputFormat === "csv") {
    let outputString =
      `"browser","version","year",` +
      `"${options.useSupports ? "supports" : "wa_compatible"}",` +
      `"release_date","engine","engine_version"`;

    outputArray.forEach((version) => {
      let outputs: {
        browser: string;
        version: string;
        year: number | string;
        release_date: string;
        engine: string;
        engine_version: string;
        supports?: string;
        wa_compatible?: boolean;
      } = {
        browser: version.browser,
        version: version.version,
        year: version.year,
        release_date: version.release_date ?? "NULL",
        engine: version.engine ?? "NULL",
        engine_version: version.engine_version ?? "NULL",
      };

      outputs = options.useSupports
        ? { ...outputs, supports: version.supports ?? "" }
        : { ...outputs, wa_compatible: version.wa_compatible };

      outputString +=
        `\n"${outputs.browser}","` +
        `${outputs.version}","` +
        `${outputs.year}","` +
        `${options.useSupports ? outputs.supports : outputs.wa_compatible}","` +
        `${outputs.release_date}","` +
        `${outputs.engine}","` +
        `${outputs.engine_version}"`;
    });

    return outputString;
  }

  return outputArray;
}

export function getTimeline(
  userOptions?: TimelineOptions & { groupBy?: "date" },
): TimelineEvent[];
export function getTimeline(
  userOptions: TimelineOptions & { groupBy: "browser" },
): BrowserTimeline;
export function getTimeline(
  userOptions?: TimelineOptions,
): TimelineEvent[] | BrowserTimeline {
  const incomingOptions = userOptions ?? {};
  const options = {
    groupBy: incomingOptions.groupBy ?? "date",
    listAllBrowsers: incomingOptions.listAllBrowsers ?? false,
    includeDownstreamBrowsers:
      incomingOptions.includeDownstreamBrowsers ?? false,
    includeKaiOS: incomingOptions.includeKaiOS ?? false,
  };

  if (
    options.includeDownstreamBrowsers === false &&
    options.includeKaiOS === true
  ) {
    throw new Error(
      "KaiOS is a downstream browser and can only be included if you include other downstream browsers. Please ensure you use `includeDownstreamBrowsers: true`.",
    );
  }

  const currentMinVersions: Record<string, CondensedTimelineEntry> = {};
  const result: TimelineEvent[] = [];

  Object.keys(timeline).forEach((changeDate) => {
    const changeList = timeline[changeDate]!;
    if (changeDate === "pre_baseline") {
      return;
    }

    const bumpedBrowsers = new Set<string>();

    changeList.forEach((change) => {
      const shortName = change[0];
      const previousEntry = currentMinVersions[shortName];
      const newVersion = change[1];

      if (!previousEntry || previousEntry[1] !== newVersion) {
        bumpedBrowsers.add(shortName);
      }

      currentMinVersions[shortName] = change;
    });

    const activeBrowsers: BrowserVersion[] = [];

    Object.keys(nameMappings).forEach((shortName) => {
      const { longName } = nameMappings[shortName]!;
      if (!options.includeKaiOS && shortName === "k") {
        return;
      }
      const isCore = coreBrowsers.some((b) => b.shortName === shortName);
      if (!options.includeDownstreamBrowsers && !isCore) {
        return;
      }

      const hasChanged = bumpedBrowsers.has(shortName);

      if (options.listAllBrowsers || hasChanged) {
        const minVersionEntry = currentMinVersions[shortName];
        if (minVersionEntry) {
          activeBrowsers.push(
            reconstructBrowserVersion(
              shortName,
              longName,
              minVersionEntry[1],
              minVersionEntry[2],
              minVersionEntry[3],
            ),
          );
        }
      }
    });

    if (activeBrowsers.length > 0) {
      const sortedActiveBrowsers = sortBrowserVersions(activeBrowsers);

      result.push({
        date: changeDate,
        browsers: sortedActiveBrowsers,
      });
    }
  });

  if (options.groupBy === "browser") {
    const browserTimeline: BrowserTimeline = {};

    Object.keys(nameMappings).forEach((shortName) => {
      const { longName } = nameMappings[shortName]!;
      if (!options.includeKaiOS && shortName === "k") {
        return;
      }
      const isCore = coreBrowsers.some((b) => b.shortName === shortName);
      if (!options.includeDownstreamBrowsers && !isCore) {
        return;
      }
      browserTimeline[longName] = [];
    });

    result.forEach((event) => {
      event.browsers.forEach((browser) => {
        const { browser: name, ...rest } = browser;
        if (browserTimeline[name]) {
          browserTimeline[name]!.push({
            date: event.date,
            ...rest,
          });
        }
      });
    });

    return browserTimeline;
  }

  return result;
}
