import { Awaitable, unindent } from "@ntnyq/utils";
import stylelint, { LinterOptions } from "stylelint";
import { Warning } from "postcss";

//#region src/types/stylelint.d.ts
type StylelintLinterResult = Omit<stylelint.LinterResult, 'cwd' | 'report'>;
/**
 * @pg
 */
type LintResultDeprecation = {
  text: string;
  reference?: string;
};
type LintResultInvalidOptionWarning = {
  text: string;
};
type LintResultParseError = Warning & {
  stylelintType: 'parseError';
};
type LintResultWarning = stylelint.Warning;
/**
 * @pg
 */
type LintResultMessage = LintResultDeprecation | LintResultInvalidOptionWarning | LintResultParseError | LintResultWarning;
/**
 * Stylelint options
 */
interface StylelintOptions<RuleOptions = any> {
  /**
   * linter options for `stylelint.lint(options)`
   */
  linterOptions?: stylelint.LinterOptions;
  /**
   * rule options
   */
  ruleOptions?: RuleOptions;
  /**
   * stylelint config
   */
  stylelintConfig?: stylelint.Config;
}
//#endregion
//#region src/types/test.d.ts
/**
 * Test case options
 */
interface TestCasesOptions<RuleOptions = any> {
  /**
   * invalid cases
   */
  invalid?: (string | InvalidTestCase<RuleOptions>)[];
  /**
   * valid cases
   */
  valid?: (string | ValidTestCase<RuleOptions>)[];
  /**
   * callback to be called after each test case
   */
  onResult?: (testCase: NormalizedTestCase<RuleOptions>, result: TestExecutionResult) => Promise<void> | void;
}
/**
 * Test execution result
 */
type TestExecutionResult = StylelintLinterResult & {
  /**
   * whether the code was fixed
   */
  fixed?: boolean;
  /**
   * if the rule fixes in multiple steps, the result of each step is present here
   */
  steps?: StylelintLinterResult[];
};
//#endregion
//#region src/types/file.d.ts
/**
 * default filename when not provided
 */
interface DefaultFilenames {
  css: string;
  less: string;
  postcss: string;
  sass: string;
  scss: string;
  styl: string;
  stylus: string;
  [key: string]: string;
}
//#endregion
//#region src/types/tester.d.ts
/**
 * Rule tester behavior options
 */
type RuleTesterBehaviorOptions = {
  /**
   * the number of times to recursively apply the rule
   *
   * @default 10
   */
  recursive?: false | number;
  /**
   * run verification after applying the fix
   *
   * @default true
   */
  verifyAfterFix?: boolean;
  /**
   * verify that fix allways changes the code
   *
   * @default true
   */
  verifyFixChanges?: boolean;
};
/**
 * Rule tester init options
 */
type RuleTesterInitOptions<RuleOptions = any> = RuleTesterBehaviorOptions & StylelintOptions<RuleOptions> & {
  /**
   * rule name to test
   */
  name: string;
  /**
   * default filenames to be used for tests
   */
  defaultFileNames?: Partial<DefaultFilenames>;
};
/**
 * Rule Tester
 * @pg
 */
interface RuleTester<RuleOptions = any> {
  /**
   * run multiple test cases
   */
  run: (options: TestCasesOptions<RuleOptions>) => Promise<void>;
  /**
   * run a single test case
   */
  each: (arg: TestCase) => Promise<{
    result: TestExecutionResult;
    testcase: NormalizedTestCase<RuleOptions>;
  }>;
  /**
   * run a single invalid test case
   */
  invalid: (arg: InvalidTestCase) => Promise<{
    result: TestExecutionResult;
    testcase: NormalizedTestCase<RuleOptions>;
  }>;
  /**
   * run a single valid test case
   */
  valid: (arg: ValidTestCase) => Promise<{
    result: TestExecutionResult;
    testcase: NormalizedTestCase<RuleOptions>;
  }>;
}
//#endregion
//#region src/types/case.d.ts
/**
 * Invalid test case
 */
type InvalidTestCase<RuleOptions = any> = string | InvalidTestCaseBase<RuleOptions>;
type InvalidTestCaseBase<RuleOptions = any> = ValidTestCaseBase<RuleOptions> & {
  /**
   * expect for {@link LintResultDeprecation}
   */
  deprecations?: number | (string | LintResultDeprecation)[] | ((deprecations: LintResultDeprecation[]) => Awaitable<void>);
  /**
   * expect for {@link LintResultInvalidOptionWarning}
   */
  invalidOptionWarnings?: number | (string | LintResultInvalidOptionWarning)[] | ((invalidOptionWarnings: LintResultInvalidOptionWarning[]) => Awaitable<void>);
  /**
   * Assert if output is expected.
   * Pass `null` to assert that the output is the same as the input.
   */
  output?: string | ((output: string, input: string) => Awaitable<void>) | null;
  /**
   * expect for {@link LintResultParseError}
   */
  parseErrors?: number | (string | LintResultParseError)[] | ((parseErrors: LintResultParseError[]) => Awaitable<void>);
  /**
   * expect for {@link LintResultWarning}
   */
  warnings?: number | (string | LintResultWarning)[] | ((warnings: LintResultWarning[]) => Awaitable<void>);
};
/**
 * Valid test case
 */
type ValidTestCase<RuleOptions = any> = string | ValidTestCaseBase<RuleOptions>;
type ValidTestCaseBase<RuleOptions = any> = RuleTesterBehaviorOptions & StylelintOptions<RuleOptions> & {
  /**
   * code to test
   */
  code: string;
  /**
   * test case description
   */
  description?: string;
  /**
   * test case filename
   */
  filename?: string;
  /**
   * test case name
   */
  name?: string;
  /**
   * only run this test case
   */
  only?: boolean;
  /**
   * skip this test case
   */
  skip?: boolean;
  /**
   * hook after run test case
   */
  after?: (this: NormalizedTestCase<RuleOptions>, result: TestExecutionResult) => Awaitable<void>;
  /**
   * hook before run test case
   */
  before?: (this: NormalizedTestCase<RuleOptions>, linterOptions: LinterOptions) => Awaitable<void>;
};
/**
 * Test case
 * @pg
 */
type TestCase<RuleOptions = any> = InvalidTestCase<RuleOptions> | ValidTestCase<RuleOptions>;
/**
 * Normalized test case
 * @pg
 */
type NormalizedTestCase<RuleOptions = any> = InvalidTestCaseBase<RuleOptions> & {
  code: string;
  filename: string;
  type: 'invalid' | 'valid';
};
//#endregion
//#region src/run.d.ts
/**
 * Shortcut to run test cases for a rule
 */
declare function run<RuleOptions = any>(options: TestCasesOptions<RuleOptions> & RuleTesterInitOptions<RuleOptions>): Promise<void>;
/**
 * Shortcut to run test cases for a rule in classic style
 */
declare function runClassic<RuleOptions = any>(ruleName: string, cases: TestCasesOptions<RuleOptions>, options?: RuleTesterInitOptions<RuleOptions>): Promise<void>;
//#endregion
//#region src/utils/resolveRuleMeta.d.ts
/**
 * Resolve rule meta by tester options
 * @param options - tester options
 * @returns a promise resolved rule meta or undefined
 */
declare function resolveRuleMeta(options: RuleTesterInitOptions): Promise<stylelint.RuleMeta | undefined>;
//#endregion
//#region src/utils/normalizeTestCase.d.ts
/**
 * Normalize test case
 *
 * @param testCase - test case
 * @param defaultFilenames - given default file name
 * @param type - case type
 * @returns normalized test case
 */
declare function normalizeTestCase<RuleOptions = any>(testCase: TestCase<RuleOptions>, defaultFilenames: Partial<DefaultFilenames>, type?: 'valid' | 'invalid'): NormalizedTestCase<RuleOptions>;
//#endregion
//#region src/utils/resolveRuleOptions.d.ts
/**
 * Normalize rule options
 *
 * @param testCase - test case
 * @param options - tester options
 * @returns normalized rule option
 */
declare function resolveRuleOptions(testCase: NormalizedTestCase, options: RuleTesterInitOptions, ruleMeta?: stylelint.RuleMeta): any[];
//#endregion
//#region src/utils/validateLintResult.d.ts
declare function validateLintResult(testCase: NormalizedTestCase, lintResult: stylelint.LintResult): Promise<void>;
//#endregion
//#region src/utils/normalizeCaseMessage.d.ts
/**
 * Normalize test case message
 *
 * @param message - message string or lint result
 * @returns normalized message
 */
declare function normalizeCaseMessage(message: string | LintResultMessage): Partial<LintResultMessage>;
//#endregion
//#region src/utils/resolveLinterOptions.d.ts
/**
 * Resolve linter options of stylelint
 *
 * @param options - tester init options
 * @param testCase - normalized test case
 * @returns resolved linter options
 */
declare function resolveLinterOptions(options: RuleTesterInitOptions, testCase: NormalizedTestCase, ruleOptions: any): stylelint.LinterOptions;
//#endregion
//#region src/utils/normalizeLinterResult.d.ts
/**
 * Normalize linter result
 *
 * @param result - linter result {@link Stylelint.LinterResult}
 */
declare function normalizeLinterResult(result: stylelint.LinterResult): {
  results: {
    deprecations: {
      text: string;
      reference?: string;
    }[];
    invalidOptionWarnings: {
      text: string;
    }[];
    parseErrors: (import("postcss").Warning & {
      stylelintType: Extract<stylelint.StylelintWarningType, "parseError">;
    })[];
    errored?: boolean;
    warnings: stylelint.Warning[];
    ignored?: boolean;
    autofixed?: boolean;
  }[];
  errored: boolean;
  code?: string;
  maxWarningsExceeded?: {
    maxWarnings: number;
    foundWarnings: number;
  };
  reportedDisables: stylelint.DisableOptionsReport;
  descriptionlessDisables?: stylelint.DisableOptionsReport;
  needlessDisables?: stylelint.DisableOptionsReport;
  invalidScopeDisables?: stylelint.DisableOptionsReport;
  ruleMetadata: {
    [ruleName: string]: Partial<stylelint.RuleMeta>;
  };
};
//#endregion
//#region src/tester.d.ts
declare function createRuleTester<RuleOptions = any>(options: RuleTesterInitOptions<RuleOptions>): RuleTester<RuleOptions>;
//#endregion
export { unindent as $, unindent, DefaultFilenames, InvalidTestCase, InvalidTestCaseBase, LintResultDeprecation, LintResultInvalidOptionWarning, LintResultMessage, LintResultParseError, LintResultWarning, NormalizedTestCase, RuleTester, RuleTesterBehaviorOptions, RuleTesterInitOptions, StylelintLinterResult, StylelintOptions, TestCase, TestCasesOptions, TestExecutionResult, ValidTestCase, ValidTestCaseBase, createRuleTester, normalizeCaseMessage, normalizeLinterResult, normalizeTestCase, resolveLinterOptions, resolveRuleMeta, resolveRuleOptions, run, runClassic, validateLintResult };