import type { FileInfo } from 'jscodeshift';
import postcss, { type Plugin } from 'postcss';
// @ts-ignore
import lessSyntax from 'postcss-less';

import { isColorRelatedProperty } from './lib/declaration';
import { isCssDeclaration } from './lib/is-css-declaration';
import { getBaseDeclarationMeta } from './lib/meta';
import { splitCssValue } from './lib/split-css-value';
import findToken from './lib/tokens';
import parseValue from './lib/value';

const POSTCSS_OPTIONS = { syntax: lessSyntax, from: undefined };

// https://github.com/postcss/postcss/blob/main/docs/writing-a-plugin.md
// https://astexplorer.net/#/2uBU1BLuJ1
const plugin = (): Plugin => {
	const processed = Symbol('processed');

	return {
		postcssPlugin: 'UsingTokens',
		AtRule(atRule) {
			// @ts-expect-error
			if (atRule[processed]) {
				return;
			}

			// @ts-expect-error: The 'variable' property does not exist on 'AtRule' according to the TypeScript definitions.
			// However, the 'postcss-less' library adds a 'variable' property to 'AtRule' when parsing LESS variables.
			// This property indicates whether the 'AtRule' is a LESS variable.
			if (atRule.variable) {
				// TODO https://hello.atlassian.net/browse/DCA11Y-637
			}

			// @ts-expect-error
			atRule[processed] = true;
		},
		Declaration: (decl) => {
			// @ts-expect-error
			if (decl[processed]) {
				return;
			}
			if (decl.value === 'none') {
				return;
			}

			const baseMeta = getBaseDeclarationMeta(decl);

			if (isCssDeclaration(decl.prop)) {
				// TODO https://hello.atlassian.net/browse/DCA11Y-637
			}

			if (isColorRelatedProperty(decl.prop)) {
				const values = splitCssValue(decl.value);
				if (!values) {
					return;
				}

				switch (decl.prop) {
					case 'box-shadow':
						const meta = values.reduce((acc: string[], curr: string) => {
							const parsedValue = parseValue(curr);
							if (!parsedValue) {
								return acc;
							}
							return [...acc, ...parsedValue.getMeta()];
						}, baseMeta);

						const token = findToken(meta);
						decl.value = `var(${token}, ${decl.value})`;
						break;
					default:
						const replacedValues = values.map((value) => {
							const parsedValue = parseValue(value);
							if (!parsedValue) {
								return value;
							}

							return parsedValue.getReplacement(baseMeta);
						});
						decl.value = replacedValues.join(' ');
						break;
				}
			}

			// @ts-expect-error
			decl[processed] = true;
		},
	};
};

export default async function transformer(file: FileInfo | string): Promise<string> {
	const processor = postcss([plugin()]);
	const src = typeof file === 'string' ? file : file.source;
	const { css } = await processor.process(src, POSTCSS_OPTIONS);
	return css;
}
