/* eslint-disable @typescript-eslint/require-await */
/* eslint-disable @typescript-eslint/no-extraneous-class */
/* eslint-disable class-methods-use-this */
import sass, { type OutputStyle } from "sass"
import UAParser from "ua-parser-js"
import { expose } from "comlink"

const parser = new UAParser(navigator.userAgent)
const quote = JSON.stringify

const modules = Object.entries({
  navigator: () => /* scss */ `
    $browser: ${quote(parser.getBrowser().name)};
    $platform: ${quote(navigator.userAgentData?.platform)};
    $userAgent: ${quote(navigator.userAgent)};
    $language: ${quote(navigator.language)};
  `,
}).map(([key, value]) => ({
  key,
  url: new URL(`sass://${key}`),
  module: {
    syntax: "scss",
    get contents() {
      return value()
    },
  } as sass.ImporterResult,
}))

@expose
export class Compiler {
  static async compileSASS(data: string, outputStyle: OutputStyle): Promise<string> {
    try {
      let { css } = sass.compileString(data, {
        style: outputStyle,
        importers: [
          {
            canonicalize: url => modules.find(m => m.key === url)?.url ?? null,
            load: url => modules.find(m => m.url.href === url.href)?.module ?? null,
          },
        ],
      })
      // https://github.com/sass/dart-sass/issues/472
      if (css.charCodeAt(0) === 0xfeff) {
        css = css.slice(1)
      }
      return css
    } catch (e) {
      if (process.env.NODE_ENV === "development") {
        console.log(data)
        console.error(e.stack)
      }
      Object.defineProperty(e, "name", {
        enumerable: true,
        value: e.name || "Error",
      })
      throw clone(e)
    }
  }
}

function clone(e: any): any {
  const keys = Reflect.ownKeys(e).concat(Reflect.ownKeys(Object.getPrototypeOf(e)))
  return Object.fromEntries(
    keys
      .map(key => [key, e[key]])
      .filter(([key, value]) => !key.startsWith("_") && typeof value !== "function")
      .map(([key, value]) => [
        key,
        typeof value === "object" && value != null ? clone(value) : value,
      ]),
  )
}

interface Brand {
  readonly brand: string
  readonly version: string
}

interface NavigatorUAData {
  readonly brands: Brand[]
  readonly mobile: boolean
  readonly platform: string
}

declare global {
  interface Navigator {
    readonly userAgentData: NavigatorUAData
  }
}
