// ////////////
// Types

export enum PseudoClassType {
  // User interaction
  HOVER = 'hover',
  ACTIVE = 'active',
  FOCUS = 'focus',
  FOCUS_WITHIN = 'focus-within',
  FOCUS_VISIBLE = 'focus-visible',

  // Link states
  VISITED = 'visited',
  LINK = 'link',
  ANY_LINK = 'any-link', // Deprecated, but some usage

  // Child and type-based selection
  FIRST_CHILD = 'first-child',
  LAST_CHILD = 'last-child',
  NTH_CHILD = 'nth-child',
  NTH_LAST_CHILD = 'nth-last-child',
  ONLY_CHILD = 'only-child',
  FIRST_OF_TYPE = 'first-of-type',
  LAST_OF_TYPE = 'last-of-type',
  NTH_OF_TYPE = 'nth-of-type',
  NTH_LAST_OF_TYPE = 'nth-last-of-type',
  ONLY_OF_TYPE = 'only-of-type',

  // Structural
  EMPTY = 'empty',
  ROOT = 'root',
  SCOPE = 'scope',
  TARGET = 'target',
  BEFORE = 'before',
  AFTER = 'after',
  FIRST_LINE = 'first-line',
  FIRST_LETTER = 'first-letter',

  // Form states
  ENABLED = 'enabled',
  DISABLED = 'disabled',
  CHECKED = 'checked',
  INDETERMINATE = 'indeterminate',
  DEFAULT = 'default',
  VALID = 'valid',
  INVALID = 'invalid',
  IN_RANGE = 'in-range',
  OUT_OF_RANGE = 'out-of-range',
  REQUIRED = 'required',
  OPTIONAL = 'optional',
  READ_ONLY = 'read-only',
  READ_WRITE = 'read-write',

  // Other pseudo-classes
  LANG = 'lang',
  DIR = 'dir'
}

export interface PseudoClass {
  type: PseudoClassType
  hasParam: boolean
  param?: string | null
  sentencePre: string
  sentencePost?: string
  helpLink?: string
  displayName?: string
}

// ////////////
// Constants

export const PSEUDO_CLASSES: PseudoClass[] = [
  // User interaction
  { type: PseudoClassType.HOVER, hasParam: false, sentencePre: 'On mouse', helpLink: 'https://developer.mozilla.org/en-US/docs/Web/CSS/:hover' },
  { type: PseudoClassType.ACTIVE, hasParam: false, sentencePre: 'When', helpLink: 'https://developer.mozilla.org/en-US/docs/Web/CSS/:active' },
  { type: PseudoClassType.FOCUS, hasParam: false, sentencePre: 'When it has the', helpLink: 'https://developer.mozilla.org/en-US/docs/Web/CSS/:focus' },
  { type: PseudoClassType.FOCUS_WITHIN, hasParam: false, sentencePre: 'When the', helpLink: 'https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-within' },
  { type: PseudoClassType.FOCUS_VISIBLE, hasParam: false, sentencePre: 'When the', helpLink: 'https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-visible' },

  // Link states
  { type: PseudoClassType.VISITED, hasParam: false, sentencePre: 'When', helpLink: 'https://developer.mozilla.org/en-US/docs/Web/CSS/:visited' },
  { type: PseudoClassType.LINK, hasParam: false, sentencePre: 'When it is a', helpLink: 'https://developer.mozilla.org/en-US/docs/Web/CSS/:link' },
  { type: PseudoClassType.ANY_LINK, hasParam: false, sentencePre: 'When it is a', helpLink: 'https://developer.mozilla.org/en-US/docs/Web/CSS/:any-link' },

  // Child and type-based selection
  { type: PseudoClassType.FIRST_CHILD, hasParam: false, sentencePre: 'When it is the', helpLink: 'https://developer.mozilla.org/en-US/docs/Web/CSS/:first-child' },
  { type: PseudoClassType.LAST_CHILD, hasParam: false, sentencePre: 'When it is the', helpLink: 'https://developer.mozilla.org/en-US/docs/Web/CSS/:last-child' },
  { type: PseudoClassType.NTH_CHILD, hasParam: true, sentencePre: 'When it is the', helpLink: 'https://developer.mozilla.org/en-US/docs/Web/CSS/:nth-child' },
  { type: PseudoClassType.NTH_LAST_CHILD, hasParam: true, sentencePre: 'When it is the', helpLink: 'https://developer.mozilla.org/en-US/docs/Web/CSS/:nth-last-child' },
  { type: PseudoClassType.ONLY_CHILD, hasParam: false, sentencePre: 'When it is the', helpLink: 'https://developer.mozilla.org/en-US/docs/Web/CSS/:only-child' },
  { type: PseudoClassType.FIRST_OF_TYPE, hasParam: false, sentencePre: 'When it is the', helpLink: 'https://developer.mozilla.org/en-US/docs/Web/CSS/:first-of-type' },
  { type: PseudoClassType.LAST_OF_TYPE, hasParam: false, sentencePre: 'When it is the', helpLink: 'https://developer.mozilla.org/en-US/docs/Web/CSS/:last-of-type' },
  { type: PseudoClassType.NTH_OF_TYPE, hasParam: true, sentencePre: 'When it is the', helpLink: 'https://developer.mozilla.org/en-US/docs/Web/CSS/:nth-of-type' },
  { type: PseudoClassType.NTH_LAST_OF_TYPE, hasParam: true, sentencePre: 'When it is the', helpLink: 'https://developer.mozilla.org/en-US/docs/Web/CSS/:nth-last-of-type' },
  { type: PseudoClassType.ONLY_OF_TYPE, hasParam: false, sentencePre: 'When it is the', helpLink: 'https://developer.mozilla.org/en-US/docs/Web/CSS/:only-of-type' },

  // Structural
  { type: PseudoClassType.EMPTY, hasParam: false, sentencePre: 'When it is', helpLink: 'https://developer.mozilla.org/en-US/docs/Web/CSS/:empty' },
  { type: PseudoClassType.ROOT, hasParam: false, sentencePre: 'When it is the', helpLink: 'https://developer.mozilla.org/en-US/docs/Web/CSS/:root' },
  { type: PseudoClassType.SCOPE, hasParam: false, sentencePre: 'When it is within', helpLink: 'https://developer.mozilla.org/en-US/docs/Web/CSS/:scope' },
  { type: PseudoClassType.TARGET, hasParam: false, sentencePre: 'When URL matches', helpLink: 'https://developer.mozilla.org/en-US/docs/Web/CSS/:target' },
  { type: PseudoClassType.BEFORE, hasParam: false, sentencePre: 'Style the', sentencePost: 'pseudo-element', helpLink: 'https://developer.mozilla.org/en-US/docs/Web/CSS/::before' },
  { type: PseudoClassType.AFTER, hasParam: false, sentencePre: 'Style the', sentencePost: 'pseudo-element', helpLink: 'https://developer.mozilla.org/en-US/docs/Web/CSS/::after' },
  { type: PseudoClassType.FIRST_LINE, hasParam: false, sentencePre: 'Style the', helpLink: 'https://developer.mozilla.org/en-US/docs/Web/CSS/::first-line' },
  { type: PseudoClassType.FIRST_LETTER, hasParam: false, sentencePre: 'Style the', helpLink: 'https://developer.mozilla.org/en-US/docs/Web/CSS/::first-letter' },

  // Form states
  { type: PseudoClassType.ENABLED, hasParam: false, sentencePre: 'When it is', helpLink: 'https://developer.mozilla.org/en-US/docs/Web/CSS/:enabled' },
  { type: PseudoClassType.DISABLED, hasParam: false, sentencePre: 'When it is', helpLink: 'https://developer.mozilla.org/en-US/docs/Web/CSS/:disabled' },
  { type: PseudoClassType.CHECKED, hasParam: false, sentencePre: 'When it is', helpLink: 'https://developer.mozilla.org/en-US/docs/Web/CSS/:checked' },
  { type: PseudoClassType.INDETERMINATE, hasParam: false, sentencePre: 'When it is', helpLink: 'https://developer.mozilla.org/en-US/docs/Web/CSS/:indeterminate' },
  { type: PseudoClassType.REQUIRED, hasParam: false, sentencePre: 'When it is', helpLink: 'https://developer.mozilla.org/en-US/docs/Web/CSS/:required' },
  { type: PseudoClassType.OPTIONAL, hasParam: false, sentencePre: 'When it is', helpLink: 'https://developer.mozilla.org/en-US/docs/Web/CSS/:optional' },

  // Other pseudo-classes
  { type: PseudoClassType.LANG, hasParam: true, sentencePre: 'When it matches', helpLink: 'https://developer.mozilla.org/en-US/docs/Web/CSS/:lang' },
  { type: PseudoClassType.DIR, hasParam: true, sentencePre: 'When the text direction is', helpLink: 'https://developer.mozilla.org/en-US/docs/Web/CSS/:dir' }
]

// ////////////
// Functions

export function toString(pseudoClass: PseudoClass): string {
  if (pseudoClass.hasParam) {
    return `:${pseudoClass.type}(${pseudoClass.param ?? ''})`
  } else {
    return `:${pseudoClass.type}`
  }
}

/**
 * @example fromString(':hover') // { type: 'hover', hasParam: false, sentencePre: 'On mouse', helpLink: 'https://developer.mozilla.org/en-US/docs/Web/CSS/:hover' }
 * @example fromString(':nth-child(2n+1) // { type: 'nth-child', hasParam: true, param: '2n+1', sentencePre: 'When it is the', helpLink: 'https://developer.mozilla.org/en-US/docs/Web/CSS/:nth-child' }
 */
export function fromString(pseudoClassStr: string): PseudoClass {
  const pseudoMatch = pseudoClassStr.match(/:([-\w]+)(\([^)]*\))?/)
  if (!pseudoMatch) {
    throw new Error(`Invalid pseudo-class: ${pseudoClassStr}`)
  }
  const type = pseudoMatch[1] as PseudoClassType
  const param = pseudoMatch[2] ? pseudoMatch[2].slice(1, -1) : null
  const pseudoClass = PSEUDO_CLASSES.find(pc => pc.type === type)
  if (!pseudoClass) {
    throw new Error(`Pseudo-class not found: ${type}`)
  }
  if (param) {
    return {
      ...pseudoClass,
      param
    }
  }
  return pseudoClass
}
