import fs from 'node:fs';
import path from 'node:path';
import { z } from 'zod';
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { config } from '../../config.js';
import { detectProject } from '../../lib/frameworks/detect.js';
import { resolveWorkspacePath } from '../../lib/sandbox.js';
import { errorResult, jsonResult, toErrorMessage } from '../shared.js';

/**
 * UI component generator. Prefers React (TSX/JSX) or Vue/Nuxt SFCs when a
 * matching frontend is detected; otherwise writes a plain HTML/CSS/JS snippet.
 */
export function registerGenerateComponentTool(server: McpServer): void {
  server.registerTool(
    'generate_component',
    {
      title: 'Generate Component',
      description:
        'Scaffold a UI component. Generates a React function component (TSX/JSX) when React is detected, ' +
        'a Vue SFC (.vue) when Vue or Nuxt is detected, or a plain HTML/CSS/JS snippet otherwise.',
      inputSchema: {
        name: z.string().min(1).describe('Component name, e.g. "UserCard" (PascalCase recommended).'),
        directory: z
          .string()
          .optional()
          .describe('Directory relative to the frontend project root. Defaults to "src/components".'),
        style: z.enum(['css-module', 'tailwind', 'none']).optional().default('css-module'),
        withTest: z.boolean().optional().default(false),
        clientComponent: z
          .boolean()
          .optional()
          .default(false)
          .describe('If true (Next.js App Router), prepends "use client" for Client Components. Ignored for Vue/Nuxt.'),
        overwrite: z.boolean().optional().default(false),
      },
    },
    async ({ name, directory, style, withTest, clientComponent, overwrite }) => {
      try {
        const detected = detectProject(config.workspaceRoot);
        const frontend = detected.frontend;

        if (frontend?.kind === 'react') {
          return jsonResult(
            generateReactComponent(
              frontend.root,
              name,
              directory,
              style,
              withTest,
              clientComponent ?? false,
              overwrite,
            ),
          );
        }

        if (frontend?.kind === 'vue' || frontend?.kind === 'nuxt') {
          return jsonResult(
            generateVueSfc(frontend.root, frontend.kind, name, directory, style, withTest, overwrite),
          );
        }

        return jsonResult(generatePlainComponent(config.workspaceRoot, name, directory, overwrite));
      } catch (error) {
        return errorResult(toErrorMessage(error));
      }
    },
  );
}

function generateReactComponent(
  frontendRoot: string,
  name: string,
  directory: string | undefined,
  style: 'css-module' | 'tailwind' | 'none',
  withTest: boolean,
  clientComponent: boolean,
  overwrite: boolean,
) {
  const typescript = fs.existsSync(path.join(frontendRoot, 'tsconfig.json'));
  const ext = typescript ? 'tsx' : 'jsx';
  const dir = resolveWorkspacePath(frontendRoot, directory ?? 'src/components');
  const componentFile = path.join(dir, `${name}.${ext}`);

  const files: string[] = [];
  assertNotExists(componentFile, overwrite);

  fs.mkdirSync(dir, { recursive: true });

  const useClient = clientComponent ? `'use client';\n\n` : '';
  const cssImport = style === 'css-module' ? `import styles from './${name}.module.css';\n` : '';
  const rootClass = style === 'css-module' ? ' className={styles.root}' : style === 'tailwind' ? ' className="p-4"' : '';

  fs.writeFileSync(
    componentFile,
    `${useClient}${cssImport}export function ${name}() {
  return (
    <div${rootClass}>
      <p>${name}</p>
    </div>
  );
}
`,
  );
  files.push(componentFile);

  if (style === 'css-module') {
    const cssFile = path.join(dir, `${name}.module.css`);
    assertNotExists(cssFile, overwrite);
    fs.writeFileSync(cssFile, `.root {\n}\n`);
    files.push(cssFile);
  }

  if (withTest) {
    const testFile = path.join(dir, `${name}.test.${ext}`);
    assertNotExists(testFile, overwrite);
    fs.writeFileSync(
      testFile,
      `import { render, screen } from '@testing-library/react';
import { describe, expect, it } from 'vitest';
import { ${name} } from './${name}';

describe('${name}', () => {
  it('renders', () => {
    render(<${name} />);
    expect(screen.getByText('${name}')).toBeInTheDocument();
  });
});
`,
    );
    files.push(testFile);
  }

  return { kind: 'react', name, files, clientComponent };
}

function generateVueSfc(
  frontendRoot: string,
  kind: 'vue' | 'nuxt',
  name: string,
  directory: string | undefined,
  style: 'css-module' | 'tailwind' | 'none',
  withTest: boolean,
  overwrite: boolean,
) {
  const typescript = fs.existsSync(path.join(frontendRoot, 'tsconfig.json'));
  const dir = resolveWorkspacePath(frontendRoot, directory ?? 'src/components');
  const componentFile = path.join(dir, `${name}.vue`);

  const files: string[] = [];
  assertNotExists(componentFile, overwrite);
  fs.mkdirSync(dir, { recursive: true });

  const langAttr = typescript ? ' lang="ts"' : '';
  const rootClass = style === 'tailwind' ? ' class="p-4"' : style === 'none' ? '' : ' class="root"';
  const styleBlock =
    style === 'none'
      ? ''
      : style === 'tailwind'
        ? ''
        : `\n<style scoped>\n.root {\n}\n</style>\n`;

  fs.writeFileSync(
    componentFile,
    `<script setup${langAttr}>
</script>

<template>
  <div${rootClass}>
    <p>${name}</p>
  </div>
</template>
${styleBlock}`,
  );
  files.push(componentFile);

  if (withTest) {
    const testExt = typescript ? 'ts' : 'js';
    const testFile = path.join(dir, `${name}.test.${testExt}`);
    assertNotExists(testFile, overwrite);
    fs.writeFileSync(
      testFile,
      `import { mount } from '@vue/test-utils';
import { describe, expect, it } from 'vitest';
import ${name} from './${name}.vue';

describe('${name}', () => {
  it('renders', () => {
    const wrapper = mount(${name});
    expect(wrapper.text()).toContain('${name}');
  });
});
`,
    );
    files.push(testFile);
  }

  return { kind, name, files };
}

function generatePlainComponent(root: string, name: string, directory: string | undefined, overwrite: boolean) {
  const dir = resolveWorkspacePath(root, directory ?? path.join('components', name));
  fs.mkdirSync(dir, { recursive: true });

  const htmlFile = path.join(dir, `${name}.html`);
  const cssFile = path.join(dir, `${name}.css`);
  const jsFile = path.join(dir, `${name}.js`);
  for (const file of [htmlFile, cssFile, jsFile]) assertNotExists(file, overwrite);

  fs.writeFileSync(
    htmlFile,
    `<div class="${name.toLowerCase()}">
  <p>${name}</p>
</div>
`,
  );
  fs.writeFileSync(cssFile, `.${name.toLowerCase()} {\n}\n`);
  fs.writeFileSync(jsFile, `// Behaviour for the ${name} component.\n`);

  return {
    kind: 'generic',
    name,
    files: [htmlFile, cssFile, jsFile],
    note: 'No React/Vue/Nuxt frontend was detected, so a plain HTML/CSS/JS snippet was generated instead.',
  };
}

function assertNotExists(file: string, overwrite: boolean): void {
  if (!overwrite && fs.existsSync(file)) {
    throw new Error(`"${file}" already exists. Pass overwrite: true to replace it.`);
  }
}
