import dedent from 'dedent'
import { describe } from 'vitest'
import { css, fetchStyles, html, retryAssertion, test, ts, txt } from '../utils'

function createSetup(transformer: 'postcss' | 'lightningcss') {
  return {
    fs: {
      'package.json': txt`
        {
          "type": "module",
          "dependencies": {
            "@tailwindcss/vite": "workspace:^",
            "tailwindcss": "workspace:^"
          },
          "devDependencies": {
            ${transformer === 'lightningcss' ? `"lightningcss": "^1.26.0",` : ''}
            "vite": "^6"
          }
        }
      `,
      'vite.config.ts': ts`
        import tailwindcss from '@tailwindcss/vite'
        import { defineConfig } from 'vite'

        export default defineConfig({
          css: ${transformer === 'postcss' ? '{}' : "{ transformer: 'lightningcss' }"},
          build: { cssMinify: false },
          plugins: [
            tailwindcss(),
            {
              name: 'recolor',
              transform(code, id) {
                if (id.includes('.css')) {
                  return code.replace(/red;/g, 'blue;')
                }
              },
            },
          ],
        })
      `,
      'index.html': html`
        <head>
          <link rel="stylesheet" href="./src/index.css" />
        </head>
        <body>
          <div class="foo [background-color:red]">Hello, world!</div>
        </body>
      `,
      'src/index.css': css`
        @reference 'tailwindcss/theme';
        @import 'tailwindcss/utilities';

        .foo {
          color: red;
        }
      `,
    },
  }
}

describe.each(['postcss', 'lightningcss'] as const)('%s', (transformer) => {
  test(`production build`, createSetup(transformer), async ({ fs, exec, expect }) => {
    await exec('pnpm vite build')

    let files = await fs.glob('dist/**/*.css')
    expect(files).toHaveLength(1)
    let [filename] = files[0]

    await fs.expectFileToContain(filename, [
      css`
        .foo {
          color: blue;
        }
      `,
      // Running the transforms on utilities generated by Tailwind might change in the future
      dedent`
        .\[background-color\:red\] {
          background-color: blue;
        }
      `,
    ])
  })

  test('dev mode', createSetup(transformer), async ({ spawn, fs, expect }) => {
    let process = await spawn('pnpm vite dev')
    await process.onStdout((m) => m.includes('ready in'))

    let url = ''
    await process.onStdout((m) => {
      let match = /Local:\s*(http.*)\//.exec(m)
      if (match) url = match[1]
      return Boolean(url)
    })

    await retryAssertion(async () => {
      let styles = await fetchStyles(url, '/index.html')
      expect(styles).toContain(css`
        .foo {
          color: blue;
        }
      `)
      // Running the transforms on utilities generated by Tailwind might change in the future
      expect(styles).toContain(dedent`
        .\[background-color\:red\] {
          background-color: blue;
        }
      `)
    })

    await retryAssertion(async () => {
      await fs.write(
        'src/index.css',
        css`
          @reference 'tailwindcss/theme';
          @import 'tailwindcss/utilities';

          .foo {
            background-color: red;
          }
        `,
      )

      let styles = await fetchStyles(url)
      expect(styles).toContain(css`
        .foo {
          background-color: blue;
        }
      `)
    })
  })

  test('watch mode', createSetup(transformer), async ({ spawn, fs, expect }) => {
    let process = await spawn('pnpm vite build --watch')
    await process.onStdout((m) => m.includes('built in'))

    await retryAssertion(async () => {
      let files = await fs.glob('dist/**/*.css')
      expect(files).toHaveLength(1)
      let [, styles] = files[0]

      expect(styles).toContain(css`
        .foo {
          color: blue;
        }
      `)
      // Running the transforms on utilities generated by Tailwind might change in the future
      expect(styles).toContain(dedent`
        .\[background-color\:red\] {
          background-color: blue;
        }
      `)
    })

    await retryAssertion(async () => {
      await fs.write(
        'src/index.css',
        css`
          @reference 'tailwindcss/theme';
          @import 'tailwindcss/utilities';

          .foo {
            background-color: red;
          }
        `,
      )

      let files = await fs.glob('dist/**/*.css')
      expect(files).toHaveLength(1)
      let [, styles] = files[0]

      expect(styles).toContain(css`
        .foo {
          background-color: blue;
        }
      `)
    })
  })
})