import { Schema } from "effect"

export const HueStep = Schema.Literals([100, 200, 300, 400, 500, 600, 700, 800, 900])
export type HueStep = Schema.Schema.Type<typeof HueStep>

export const BaseHue = Schema.Literals(["gray", "red", "orange", "yellow", "green", "cyan", "blue", "purple"])
export type BaseHue = Schema.Schema.Type<typeof BaseHue>

export const HueAlias = Schema.Literals(["accent", "interactive", "neutral"])
export type HueAlias = Schema.Schema.Type<typeof HueAlias>

export const ActionVariant = Schema.Literals(["primary", "destructive"])
export type ActionVariant = Schema.Schema.Type<typeof ActionVariant>

export const ActionState = Schema.Literals(["disabled", "pressed", "focused", "selected", "hovered"])
export type ActionState = Schema.Schema.Type<typeof ActionState>
export type ActionStateKey = `$${ActionState}`

export const FormfieldState = ActionState
export type FormfieldState = ActionState
export type FormfieldStateKey = `$${FormfieldState}`

export const FeedbackKind = Schema.Literals(["error", "warning", "success", "info"])
export type FeedbackKind = Schema.Schema.Type<typeof FeedbackKind>

const Mode = Schema.Literals(["light", "dark"])
export type Mode = Schema.Schema.Type<typeof Mode>

const HexColor = Schema.String.check(Schema.isPattern(/^#(?:[\da-f]{3}|[\da-f]{4}|[\da-f]{6}|[\da-f]{8})$/i))

const ColorValue = Schema.Union([
  HexColor,
  Schema.Literal("transparent"),
  Schema.TemplateLiteral(["$", Schema.NonEmptyString]),
])

export const HueName = Schema.Union([BaseHue, HueAlias])
export type HueName = Schema.Schema.Type<typeof HueName>
export const CategoricalDefinition = Schema.Array(HueName).check(Schema.isMinLength(1))
export type CategoricalDefinition = Schema.Schema.Type<typeof CategoricalDefinition>
const HueColorValue = Schema.Union([HexColor, Schema.TemplateLiteral(["$hue.", HueName, ".", HueStep])])

const ContextKey = Schema.Literals(["@context:elevated", "@context:overlay"])
export type ContextKey = Schema.Schema.Type<typeof ContextKey>

const HueScaleDefinition = Schema.Record(HueStep, HexColor)
const HueValueDefinition = Schema.Union([Schema.TemplateLiteral(["$hue.", HueName]), HueScaleDefinition])

const HueDefinition = Schema.Struct({
  gray: HueValueDefinition,
  red: HueValueDefinition,
  orange: HueValueDefinition,
  yellow: HueValueDefinition,
  green: HueValueDefinition,
  cyan: HueValueDefinition,
  blue: HueValueDefinition,
  purple: HueValueDefinition,
  accent: HueValueDefinition,
  interactive: HueValueDefinition,
  neutral: HueValueDefinition,
})
export type HueDefinition = Schema.Schema.Type<typeof HueDefinition>

const HueOverrideDefinition = Schema.Struct({
  gray: Schema.optional(HueValueDefinition),
  red: Schema.optional(HueValueDefinition),
  orange: Schema.optional(HueValueDefinition),
  yellow: Schema.optional(HueValueDefinition),
  green: Schema.optional(HueValueDefinition),
  cyan: Schema.optional(HueValueDefinition),
  blue: Schema.optional(HueValueDefinition),
  purple: Schema.optional(HueValueDefinition),
  accent: Schema.optional(HueValueDefinition),
  interactive: Schema.optional(HueValueDefinition),
  neutral: Schema.optional(HueValueDefinition),
})
export type HueOverrideDefinition = Schema.Schema.Type<typeof HueOverrideDefinition>

const StatefulColorDefinition = Schema.Struct({
  default: Schema.optional(ColorValue),
  $hovered: Schema.optional(ColorValue),
  $focused: Schema.optional(ColorValue),
  $pressed: Schema.optional(ColorValue),
  $selected: Schema.optional(ColorValue),
  $disabled: Schema.optional(ColorValue),
})
export type StatefulColorDefinition = Schema.Schema.Type<typeof StatefulColorDefinition>

export type FormfieldColorDefinition = StatefulColorDefinition

const ActionColorDefinition = Schema.Struct({
  primary: Schema.optional(StatefulColorDefinition),
  destructive: Schema.optional(StatefulColorDefinition),
})

const TextFeedbackDefinition = Schema.Struct({
  default: Schema.optional(ColorValue),
  subdued: Schema.optional(ColorValue),
})

const BackgroundFeedbackDefinition = Schema.Struct({
  default: Schema.optional(ColorValue),
})

const TextDefinition = Schema.Struct({
  default: Schema.optional(ColorValue),
  subdued: Schema.optional(ColorValue),
  action: Schema.optional(ActionColorDefinition),
  formfield: Schema.optional(StatefulColorDefinition),
  feedback: Schema.optional(
    Schema.Struct({
      error: Schema.optional(TextFeedbackDefinition),
      warning: Schema.optional(TextFeedbackDefinition),
      success: Schema.optional(TextFeedbackDefinition),
      info: Schema.optional(TextFeedbackDefinition),
    }),
  ),
})
export type TextDefinition = Schema.Schema.Type<typeof TextDefinition>

const BackgroundDefinition = Schema.Struct({
  default: Schema.optional(ColorValue),
  surface: Schema.optional(
    Schema.Struct({
      offset: Schema.optional(ColorValue),
      overlay: Schema.optional(ColorValue),
    }),
  ),
  action: Schema.optional(ActionColorDefinition),
  formfield: Schema.optional(StatefulColorDefinition),
  feedback: Schema.optional(
    Schema.Struct({
      error: Schema.optional(BackgroundFeedbackDefinition),
      warning: Schema.optional(BackgroundFeedbackDefinition),
      success: Schema.optional(BackgroundFeedbackDefinition),
      info: Schema.optional(BackgroundFeedbackDefinition),
    }),
  ),
})
export type BackgroundDefinition = Schema.Schema.Type<typeof BackgroundDefinition>

export const SyntaxToken = Schema.Literals([
  "comment",
  "keyword",
  "function",
  "variable",
  "string",
  "number",
  "type",
  "operator",
  "punctuation",
])
export type SyntaxToken = Schema.Schema.Type<typeof SyntaxToken>
export const SyntaxDefinition = Schema.Record(SyntaxToken, Schema.optionalKey(HueColorValue))
export type SyntaxDefinition = Schema.Schema.Type<typeof SyntaxDefinition>

export const MarkdownToken = Schema.Literals([
  "text",
  "heading",
  "link",
  "linkText",
  "code",
  "blockQuote",
  "emphasis",
  "strong",
  "horizontalRule",
  "listItem",
  "listEnumeration",
  "image",
  "imageText",
  "codeBlock",
])
export type MarkdownToken = Schema.Schema.Type<typeof MarkdownToken>
export const MarkdownDefinition = Schema.Record(MarkdownToken, Schema.optionalKey(HueColorValue))
export type MarkdownDefinition = Schema.Schema.Type<typeof MarkdownDefinition>

const DiffDefinition = Schema.Struct({
  text: Schema.optional(
    Schema.Struct({
      added: Schema.optional(ColorValue),
      removed: Schema.optional(ColorValue),
      context: Schema.optional(ColorValue),
      hunkHeader: Schema.optional(ColorValue),
    }),
  ),
  background: Schema.optional(
    Schema.Struct({
      added: Schema.optional(ColorValue),
      removed: Schema.optional(ColorValue),
      context: Schema.optional(ColorValue),
    }),
  ),
  highlight: Schema.optional(
    Schema.Struct({ added: Schema.optional(ColorValue), removed: Schema.optional(ColorValue) }),
  ),
  lineNumber: Schema.optional(
    Schema.Struct({
      text: Schema.optional(ColorValue),
      background: Schema.optional(
        Schema.Struct({ added: Schema.optional(ColorValue), removed: Schema.optional(ColorValue) }),
      ),
    }),
  ),
})
export type DiffDefinition = Schema.Schema.Type<typeof DiffDefinition>

const ThemeTokensDefinition = Schema.Struct({
  text: Schema.optional(TextDefinition),
  background: Schema.optional(BackgroundDefinition),
  border: Schema.optional(Schema.Struct({ default: Schema.optional(ColorValue) })),
  scrollbar: Schema.optional(Schema.Struct({ default: Schema.optional(ColorValue) })),
  diff: Schema.optional(DiffDefinition),
  syntax: Schema.optional(SyntaxDefinition),
  markdown: Schema.optional(MarkdownDefinition),
})
export type ThemeTokensDefinition = Schema.Schema.Type<typeof ThemeTokensDefinition>

const ThemeDefinitionFields = Schema.Struct({
  hue: HueDefinition,
  categorical: Schema.optional(CategoricalDefinition),
  ...ThemeTokensDefinition.fields,
  "@context:elevated": Schema.optional(ThemeTokensDefinition),
  "@context:overlay": Schema.optional(ThemeTokensDefinition),
})
export const ThemeDefinition = ThemeDefinitionFields
export type ThemeDefinition = Schema.Schema.Type<typeof ThemeDefinition>

const FileThemeDefinition = Schema.Struct({
  hue: Schema.optional(HueOverrideDefinition),
  categorical: Schema.optional(CategoricalDefinition),
  ...ThemeTokensDefinition.fields,
  "@context:elevated": Schema.optional(ThemeTokensDefinition),
  "@context:overlay": Schema.optional(ThemeTokensDefinition),
})
export type FileThemeDefinition = Schema.Schema.Type<typeof FileThemeDefinition>

const MergeModeDefinition = Schema.Struct({
  mergeMode: Schema.Literal(true),
  hue: Schema.optional(HueOverrideDefinition),
  categorical: Schema.optional(CategoricalDefinition),
  ...ThemeTokensDefinition.fields,
  "@context:elevated": Schema.optional(ThemeTokensDefinition),
  "@context:overlay": Schema.optional(ThemeTokensDefinition),
})
export type MergeModeDefinition = Schema.Schema.Type<typeof MergeModeDefinition>
export const ModeDefinition = Schema.Union([MergeModeDefinition, FileThemeDefinition])
export type ModeDefinition = Schema.Schema.Type<typeof ModeDefinition>

const FileMetadata = {
  $schema: Schema.optional(Schema.String),
  version: Schema.Literal(2),
  standalone: Schema.optional(Schema.Boolean),
}
export const ThemeDocument = Schema.Union([
  Schema.Struct({ ...FileMetadata, light: ModeDefinition, dark: Schema.optional(ModeDefinition) }),
  Schema.Struct({ ...FileMetadata, light: Schema.optional(ModeDefinition), dark: ModeDefinition }),
])
export type ThemeDocument = Schema.Schema.Type<typeof ThemeDocument>
