import convertCSS from '~/convertCSS';
import { MESSAGES } from '~/modules/constants';
import { invariant } from '~/modules/invariant';
import { resolveColor } from '~/modules/parsed-color';
import { isString } from '~/modules/validators';
import rotate from '~/rotate';

import { ColorType } from '~/types';

export type Scheme =
  | 'analogous'
  | 'complementary'
  | 'rectangle'
  | 'split'
  | 'split-complementary'
  | 'square'
  | 'tetradic'
  | 'triadic';

export interface SchemeOptions {
  /**
   * Output color format.
   *
   * If not specified, the output will use the same format as the input color.
   */
  format?: ColorType;
  /**
   * The scheme to generate.
   * @default 'complementary'
   */
  scheme?: Scheme;
}

/**
 * Get a color scheme based on the input color.
 *
 * @param input - The base color string.
 * @param schemeOrOptions - The scheme type or options object.
 * @returns An array of color strings forming the scheme.
 */
export default function scheme(input: string, schemeOrOptions?: Scheme | SchemeOptions): string[] {
  invariant(isString(input), MESSAGES.inputString);
  const { format, scheme: schemeType = 'complementary' } = isString(schemeOrOptions)
    ? { scheme: schemeOrOptions }
    : (schemeOrOptions ?? {});

  const output = format ?? resolveColor(input).type;

  const colors: string[] = [];

  switch (schemeType) {
    case 'analogous': {
      colors.push(rotate(input, -30), input, rotate(input, 30));
      break;
    }
    case 'complementary': {
      colors.push(input, rotate(input, 180));
      break;
    }

    case 'split':
    case 'split-complementary': {
      colors.push(input, rotate(input, 150), rotate(input, 210));
      break;
    }
    case 'triadic': {
      colors.push(input, rotate(input, 120), rotate(input, 240));
      break;
    }

    case 'tetradic':
    case 'rectangle': {
      colors.push(input, rotate(input, 60), rotate(input, 180), rotate(input, 240));
      break;
    }
    case 'square': {
      colors.push(input, rotate(input, 90), rotate(input, 180), rotate(input, 270));
      break;
    }
    default: {
      throw new TypeError('invalid type');
    }
  }

  return colors.map(color => convertCSS(color, output));
}
