import type { Context } from '@bamboocss/core'
import { compact } from '@bamboocss/shared'
import type { ArtifactFilters } from '@bamboocss/types'
import { stringify } from 'javascript-stringify'
import { outdent } from 'outdent'

export function generatePattern(ctx: Context, filters?: ArtifactFilters) {
  if (ctx.patterns.isEmpty()) return

  const details = ctx.patterns.filterDetails(filters)

  return details.map((pattern) => {
    const { baseName, config, dashName, upperName, styleFnName, blocklistType } = pattern
    const { properties, transform, cssProps, description, defaultValues, deprecated } = config

    const patternConfigFn = stringify(compact({ transform, defaultValues })) ?? ''

    const helperImports = ['getPatternStyles, createPatternFns, memo']
    // depending on the esbuild result, sometimes the transform function couldi include polyfills (e.g. __spreadValues)
    if (patternConfigFn.includes('__spreadValues')) {
      helperImports.push('__spreadValues')
    }
    if (patternConfigFn.includes('__objRest')) {
      helperImports.push('__objRest')
    }

    return {
      name: dashName,
      dts: outdent`
      ${ctx.file.importType('SystemStyleObject, ConditionalValue', '../types/index')}
      ${ctx.file.importType('Properties', '../types/csstype')}
      ${ctx.file.importType('SystemProperties', '../types/style-props')}
      ${ctx.file.importType('DistributiveOmit', '../types/system-types')}
      ${ctx.file.importType('Tokens', '../tokens/index')}

      export interface ${upperName}Properties {
         ${Object.keys(properties ?? {})
           .map((key) => {
             const value = properties![key]
             const typeString = ctx.patterns.getPropertyType(value)
             return `${key}?: ${typeString}`
           })
           .join('\n\t')}
      }

      ${outdent`
          interface ${upperName}Styles extends ${upperName}Properties${
            cssProps === 'none'
              ? ''
              : `, DistributiveOmit<SystemStyleObject, keyof ${upperName}Properties ${blocklistType}>`
          } {}

          interface ${upperName}PatternFn {
            (styles?: ${upperName}Styles): string
            raw: (styles?: ${upperName}Styles) => SystemStyleObject
          }

          ${ctx.file.jsDocComment(description, { deprecated })}
          export declare const ${baseName}: ${upperName}PatternFn;
          `}

     `,
      js: outdent`
    ${ctx.file.import(helperImports.join(', '), '../helpers')}
    ${ctx.file.import('css', '../css/index')}
    ${ctx.file.import('token', '../tokens/index')}

    /**
     * The transform's token lookup, answered by the generated tokens artifact.
     *
     * Read from there rather than from a copy emitted here, so the browser cannot disagree with
     * the build about a token's variable name — both come from the same generated source. The
     * artifact is shared with any other \`token()\` use in the app, so it is deduped rather than
     * paid twice.
     */
    const patternHelpers = /* @__PURE__ */ createPatternFns((path, fallback) => token(path) ?? fallback)

    const ${baseName}Config = ${patternConfigFn
      .replace(`{transform`, `{\ntransform`)
      .replace(`,defaultValues`, `,\ndefaultValues`)}

    export const ${styleFnName} = (styles = {}) => {
      const _styles = getPatternStyles(${baseName}Config, styles)
      return ${baseName}Config.transform(_styles, patternHelpers)
    }

    export const ${baseName} = /* @__PURE__ */ memo((styles) => css(${styleFnName}(styles)))
    ${baseName}.raw = ${styleFnName}
    `,
    }
  })
}
