import {
  Arg,
  Args,
  ArgsType,
  Field,
  FieldResolver,
  Query,
  Resolver,
  Root,
} from "@aet/gql-tools/macro"
import { GraphQLError } from "graphql"
import { Section } from "~/shared/models/index"
import { compileSASS } from "../../compilerBridge"
import { GLOBAL_STYLE_ID } from "~/shared"
import { localDb } from "~/background/couchdb"

@ArgsType()
class CompileCode {
  @Field() code!: string
  @Field() includeGlobal!: boolean
  @Field() compressed!: boolean
  @Field() cache!: boolean
}

@Resolver(() => Section)
export class SectionResolver {
  @FieldResolver()
  async compiledCode(@Root() section: Section, @Arg() cache = true): Promise<string> {
    return this.compileCode({
      code: section.code,
      includeGlobal: true,
      compressed: true,
      cache,
    })
  }

  @Query()
  async compileCode(
    @Args() { code, includeGlobal, compressed, cache }: CompileCode,
  ): Promise<string> {
    let prefix = ""
    if (includeGlobal) {
      const style = await localDb.get(GLOBAL_STYLE_ID)
      const globalStyle =
        style?.state === null && style?.enabled && style?.sections[0].code
      prefix = globalStyle ? globalStyle + "\n" : ""
    }

    const source = prefix + code
    try {
      return await compileSASS(source, compressed ? "compressed" : "expanded", cache)
    } catch (e) {
      if (e?.name !== "Error") {
        throw e
      }

      throw new GraphQLError(e.message, {
        extensions: getErrorExtension(e as SassException),
      })
    }
  }
}

export type ErrorExtension = ReturnType<typeof getErrorExtension>

function getErrorExtension(e: SassException) {
  return {
    code: "BAD_USER_INPUT" as const,
    span: {
      start: {
        line: e.span.start.line,
        column: e.span.start.column,
        offset: e.span.start.offset,
      },
      end: {
        line: e.span.end.line,
        column: e.span.end.column,
        offset: e.span.end.offset,
      },
    },
  }
}

interface SourceFile {
  url: null
  _cachedLine: null
  _decodedChars: Uint32Array
  _lineStarts: number[]
}

interface FileLocation {
  file: SourceFile
  offset: number
  column: number
  line: number
}

interface FileSpan {
  _end: number
  _file$_start: number
  context: string
  end: FileLocation
  file: SourceFile
  start: FileLocation
  text: string
  url: null
}

interface SassException extends Error {
  _dartException: {
    _span: FileSpan
    _span_exception$_message: string
  }
  sassMessage: string
  sassStack: string
  span: FileSpan
}
