import type {StylisticCustomizeOptions} from '@stylistic/eslint-plugin'
import type {ParserOptions} from '@typescript-eslint/types'
import type {FlatGitignoreOptions} from 'eslint-config-flat-gitignore'
import type {Config} from './config'
import type {AstroOptions, MarkdownOptions} from './configs'

/**
  Flattens an object type to a mapped type with the same keys and values.
 */
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
export type Flatten<T> = T extends Function ? T : {[K in keyof T]: T[K]} & {}

export interface JsxA11yOptions extends OptionsOverrides {
  // Add future a11y-specific options here
}

export interface JsxOptions {
  /**
   * Enable JSX accessibility rules.
   *
   * Requires installing:
   * - `eslint-plugin-jsx-a11y`
   *
   * Can be a boolean or an object for custom options and overrides.
   * @default false
   */
  a11y?: boolean | JsxA11yOptions
}

/**
 * Provides an option to override the `files` option in the ESLint configuration.
 * This allows customizing the glob patterns used to include files in the linting process.
 */
export interface OptionsFiles {
  /**
   * Override the `files` option to provide custom globs.
   */
  files?: Config['files']
}

/**
 * Provides an option to enable editor-specific rules in the ESLint configuration.
 * When `isInEditor` is `true`, the ESLint configuration will include rules that are
 * specific to the editor environment, such as rules related to editor features or
 * integrations.
 */
export interface OptionsIsInEditor {
  /**
   * Enable editor specific rules.
   */
  isInEditor?: boolean
}

/**
 * Provides an option to override the rules in the ESLint configuration.
 * This allows customizing the rules that are applied during the linting process.
 */
export interface OptionsOverrides {
  /**
   * Override rules.
   */
  overrides?: Config['rules']
}

/**
 * Options for configuring the Perfectionist sorting behavior.
 */
export interface OptionsPerfectionist {
  /**
   * Whether to sort named exports.
   *
   * @default true
   */
  sortNamedExports?: boolean
  /**
   * Whether to sort named imports.
   * @default true
   */
  sortNamedImports?: boolean
  /**
   * Whether to sort exports.
   * @default true
   */
  sortExports?: boolean
  /**
   * Whether to sort imports.
   * @default true
   */
  sortImports?: boolean
}

/**
 * Provides an option to enable erasable syntax only rules in the ESLint configuration.
 * When `erasableSyntaxOnly` is set to `true`, the ESLint configuration will include
 * rules from the `eslint-plugin-erasable-syntax-only` plugin, which focuses on enforcing
 * syntax that can be safely removed without affecting the program's behavior.
 */
export interface OptionsTypeScriptErasableSyntaxOnly {
  /**
   * Enable erasable syntax only rules.
   *
   * @see https://github.com/JoshuaKGoldberg/eslint-plugin-erasable-syntax-only
   * @default false
   */
  erasableSyntaxOnly?: boolean
}

/**
 * Provides options to configure the TypeScript parser and type-aware linting rules.
 *
 * The `parserOptions` property allows specifying additional options to be passed to the TypeScript parser.
 *
 * The `typeAware` property allows configuring which files should be type-aware for linting purposes. This includes
 * specifying glob patterns for files that should be type-aware, as well as files that should be ignored from
 * type-aware linting.
 */
export interface OptionsTypeScriptParserOptions {
  /**
   * Additional parser options specific tos TypeScript.
   */
  parserOptions?: Partial<ParserOptions>

  /**
   * Override type aware rules.
   */
  typeAware?: {
    /**
     * Glob patterns for files that should be type aware.
     * @default ['**\/*.{ts,tsx}']
     */
    files?: string[]

    /**
     * Glob patterns for files that should not be type aware.
     * @default ['**\/*.md\/**', '**\/*.astro/*.ts']
     */
    ignores?: string[]
  }
}

/**
 * Provides options to configure type-aware linting rules when using TypeScript.
 *
 * The `tsconfigPath` option specifies the path to the TypeScript configuration file that should be used for type-aware linting.
 *
 * The `typeAware` option allows overriding the type-aware rules that are applied during linting.
 */
export interface OptionsTypeScriptWithTypes {
  /**
   * When this options is provided, type aware rules will be enabled.
   * @see https://typescript-eslint.io/linting/typed-linting/
   */
  tsconfigPath?: string

  /**
   * Override type aware rules.
   */
  typeAware?: OptionsOverrides
}

/**
 * Represents the options for configuring TypeScript support in the ESLint configuration.
 */
export type OptionsTypeScript =
  | (OptionsTypeScriptParserOptions & OptionsOverrides & OptionsTypeScriptErasableSyntaxOnly)
  | (OptionsTypeScriptWithTypes & OptionsOverrides & OptionsTypeScriptErasableSyntaxOnly)

export interface OptionsStylistic {
  stylistic?: boolean | StylisticConfig
}

export interface StylisticConfig extends Pick<
  StylisticCustomizeOptions,
  'indent' | 'quotes' | 'jsx' | 'semi'
> {}

/**
 * Configuration options for the ESLint setup.
 *
 * @remarks
 * This type represents the flattened configuration options that can be used to customize
 * the ESLint behavior. It extends the base {@link Config} type (excluding 'files' property) and
 * provides additional customization options.
 *
 * @example
 * ```ts
 * const options: Options = {
 *   gitignore: true,
 *   typescript: true,
 *   vitest: false
 * };
 * ```
 */
export type Options = Flatten<
  // @keep-sorted
  {
    /**
     * Enable Astro support.
     *
     * @default false
     */
    astro?: boolean | AstroOptions

    /**
     * Enable gitignore support.
     *
     * @see https://github.com/antfu/eslint-config-flat-gitignore
     * @default true
     */
    gitignore?: boolean | FlatGitignoreOptions

    /**
     * Extend the global ignores.
     *
     * Passing an array to extends the ignores.
     * Passing a function to modify the default ignores.
     *
     * @default []
     */
    ignores?: string[] | ((originals: string[]) => string[])

    /**
     * Options to override the behavior of import-related rules.
     *
     * @default true
     */
    imports?: boolean | OptionsOverrides

    /**
     * Indicates whether the code is being executed in an editor environment.
     */
    isInEditor?: boolean

    /**
     * Options to override the behavior of JavaScript-related rules.
     */
    javascript?: OptionsOverrides

    /**
     * Options to override the behavior of linting JSON, JSON5 and JSONC files.
     */
    jsonc?: boolean | OptionsOverrides

    /**
     * Options to override the behavior of linting JSX files.
     *
     * @default true
     */
    jsx?: boolean | JsxOptions

    /**
     * Options to override the behavior of linting Markdown files.
     *
     * @remarks
     * Enable comprehensive Markdown linting with support for CommonMark and GitHub Flavored Markdown,
     * frontmatter parsing, and code block extraction.
     *
     * @example
     * ```typescript
     * // Enable with defaults
     * const config = defineConfig({ markdown: true });
     *
     * // Documentation site configuration
     * const config = defineConfig({
     *   markdown: {
     *     language: 'gfm',
     *     frontmatter: 'yaml',
     *     processor: { enabled: true, extractCodeBlocks: true }
     *   }
     * });
     * ```
     *
     * @default true
     */
    markdown?: boolean | MarkdownOptions

    /**
     * Enable Next.js support.
     *
     * Requires installing:
     * - `@next/eslint-plugin-next`
     *
     * @default false
     */
    nextjs?: boolean | OptionsOverrides

    /**
     * Enable support for package.json files.
     *
     * @default false
     */
    packageJson?: boolean | OptionsFiles

    /**
     * Options to override the behavior of Perfectionist sorting rules.
     */
    perfectionist?: boolean | OptionsPerfectionist

    /**
     * Enable pnpm (workspace/catalogs) support.
     *
     * Currently it's disabled by default, as it's still experimental.
     * In the future it will be smartly enabled based on the project usage.
     *
     * @see https://github.com/antfu/pnpm-workspace-utils
     * @experimental
     * @default false
     */
    pnpm?: boolean

    /**
     * Options to override the behavior of the Prettier code formatter.
     */
    prettier?: boolean | OptionsOverrides

    /**
     * Enable react rules.
     *
     * Requires installing:
     * - `@eslint-react/eslint-plugin`
     * - `eslint-plugin-react-hooks`
     * - `eslint-plugin-react-refresh`
     *
     * @default false
     */
    react?: boolean | OptionsOverrides

    /**
     * Options to override the behavior of RegExp linting rules.
     */
    regexp?: boolean | OptionsOverrides

    /**
     * Enable stylistic rules.
     *
     * Requires installing:
     * - `@stylistic/eslint-plugin`
     *
     * @see https://eslint.style/
     * @default true
     */
    stylistic?: boolean | (StylisticConfig & OptionsOverrides)

    /**
     * Options to override the behavior of the TOML parser and linting rules.
     */
    toml?: boolean | OptionsOverrides

    /**
     * Enable TypeScript support.
     *
     * Pass options to enable support for the TypeScript language and project services.
     *
     * @default auto-detect based on the dependencies
     */
    typescript?: OptionsTypeScript | boolean

    /**
     * Enable or override unicorn rules.
     *
     * @default true
     */
    unicorn?: boolean | (OptionsFiles & OptionsOverrides)

    /**
     * Enable support for vitest.
     *
     * @default false
     */
    vitest?: boolean | OptionsOverrides

    /**
     * Enable support for yaml.
     *
     * @default true
     */
    yaml?: boolean | OptionsOverrides
  } & Omit<Config, 'files'>
>
