/* eslint-disable @typescript-eslint/require-await */
/**
 * Microservice generator - auto-creates microservice folder structure
 * Generates boilerplate code for domain-driven microservices
 */
import { Logger } from '../config/logger.js';
import fs from '../node-singletons/fs.js';
import * as path from '../node-singletons/path.js';
/**
 * Microservice code generator
 */
export const MicroserviceGenerator = Object.freeze(() => {
    let instance;
    return {
        getInstance() {
            instance ??= this.create();
            return instance;
        },
        /**
         * Create a new microservice generator instance
         */
        create() {
            return {
                /**
                 * Generate microservices folder structure
                 */
                async generate(options) {
                    const { domain, services, basePort = 3001, version = '1.0.0' } = options;
                    Logger.info(`\n🏗️  Generating microservices for domain: ${domain}`);
                    Logger.info(`📦 Services: ${services.join(', ')}\n`);
                    await Promise.all(services.map(async (serviceName, i) => {
                        const servicePort = basePort + i;
                        return generateService({
                            domain,
                            serviceName,
                            port: servicePort,
                            version,
                        });
                    }));
                    // Generate shared utils
                    await generateSharedUtils(domain);
                    // Generate docker-compose for local dev
                    await generateDockerCompose(domain, services, basePort);
                    Logger.info(`✅ Microservices generated in services/${domain}/\n`);
                },
            };
        },
    };
})();
const pascalCase = (str) => {
    return str
        .split(/[-_]/)
        .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
        .join('');
};
const buildReadmeHeader = (serviceName, domain, port) => {
    return `# ${serviceName} Microservice

Domain: \`${domain}\`

## Description

${serviceName} microservice for ZinTrust framework.

## Port

\`\`\`
${port}
\`\`\``;
};
const buildReadmeGettingStarted = (domain, serviceName) => {
    return `## Getting Started

### Development

\`\`\`bash
cd services/${domain}/${serviceName}
npm install
npm run dev
\`\`\`

### Testing

\`\`\`bash
npm test
\`\`\`

### Build

\`\`\`bash
npm run build
\`\`\``;
};
const buildReadmeApiInfo = (serviceName, port) => {
    return `## API Endpoints

- \`GET /api/${serviceName}/health\` - Health check

## Environment Variables

\`\`\`bash
SERVICE_NAME=${serviceName}
SERVICE_PORT=${port}
DB_CONNECTION=postgresql
\`\`\`

## Inter-Service Communication

Call another service:

\`\`\`typescript
import { MicroserviceManager } from './MicroserviceManager';

const manager = MicroserviceManager.getInstance();
const response = await manager.callService('other-service', {
  method: 'GET',
  path: '/api/other-service/data',
});
\`\`\`

## Dependencies

- ZinTrust Framework`;
};
const generateServiceConfig = async (serviceDir, serviceName, port, version) => {
    const config = {
        name: serviceName,
        version,
        port,
        description: `${serviceName} microservice`,
        dependencies: [],
        healthCheck: '/health',
    };
    fs.writeFileSync(path.join(serviceDir, 'service.config.json'), JSON.stringify(config, null, 2));
};
const generateServiceKernel = async (serviceDir, serviceName, domain) => {
    const code = `import { Application, Kernel, type IKernel } from '../index.js';

/**
 * ${serviceName} Microservice Kernel
 * Domain: ${domain}
 */
export const ${pascalCase(serviceName)}Kernel = {
  create(app: Application): IKernel {
    const kernel = Kernel.create(app);

    // Register service-specific logic here
    // kernel.router.get('/health', (req, res) => res.json({ status: 'ok' }));

    return kernel;
  }
};

export default ${pascalCase(serviceName)}Kernel.create(Application.create());
`;
    fs.writeFileSync(path.join(serviceDir, 'src', 'Kernel.ts'), code);
};
const generateServiceRoutes = async (serviceDir, serviceName) => {
    const code = `import { Router, type IRouter } from '../index.js';

/**
 * ${serviceName} service routes
 */
export function registerRoutes(router: IRouter): void {
  Router.group(router, '/api/${serviceName}', (group: IRouter): void => {
    Router.get(group, '/health', '${pascalCase(serviceName)}Controller@health');

    // TODO: Add your routes here
    // Router.get(group, '/', '${pascalCase(serviceName)}Controller@index');
  });
}

export default registerRoutes;
`;
    fs.writeFileSync(path.join(serviceDir, 'src', 'routes', 'index.ts'), code);
};
const generateServiceController = async (serviceDir, serviceName) => {
    const code = `import { Controller, type IController, type IRequest, type IResponse } from '../index.js';

/**
 * ${pascalCase(serviceName)} Controller
 */
export interface I${pascalCase(serviceName)}Controller extends IController {
  health(req: IRequest, res: IResponse): Promise<void>;
}

export const ${pascalCase(serviceName)}Controller = {
  create(): I${pascalCase(serviceName)}Controller {
    return {
      ...Controller.create(),
      /**
       * Health check
       */
      async health(_req: IRequest, res: IResponse): Promise<void> {
        res.json({ status: 'ok', service: '${serviceName}' }, 200);
      },
    };
  }
};
`;
    fs.writeFileSync(path.join(serviceDir, 'src', 'http', 'Controllers', 'index.ts'), code);
};
const generateServiceModel = async (serviceDir, serviceName) => {
    const modelName = pascalCase(serviceName.slice(0, -1)); // Remove 's'
    const code = `import { Model } from '../index.js';

/**
 * ${modelName} Model
 */
export const ${modelName} = Model.define('${serviceName}', {
  fillable: [
    // TODO: Add fillable attributes
  ],
  hidden: [
    // 'password',
  ],
  casts: {
    // 'created_at': 'datetime',
  },
});
`;
    fs.writeFileSync(path.join(serviceDir, 'src', 'models', 'index.ts'), code);
};
const generateServiceTest = async (serviceDir, serviceName) => {
    const code = `import { describe, it, expect } from 'vitest';

/**
 * ${serviceName} tests
 */
describe('${serviceName} service', () => {
  it('should have health check endpoint', async () => {
    // TODO: Implement tests
    expect(true).toBe(true);
  });
});
`;
    fs.writeFileSync(path.join(serviceDir, 'tests', 'Feature', 'Example.test.ts'), code);
};
const generateServicePackageJson = async (serviceDir, serviceName, version) => {
    const pkg = {
        name: `@zintrust/${serviceName}`,
        version,
        description: `${serviceName} microservice`,
        type: 'module',
        scripts: {
            dev: 'tsx watch src/index.ts',
            build: 'tsc',
            test: 'vitest',
        },
    };
    fs.writeFileSync(path.join(serviceDir, 'package.json'), JSON.stringify(pkg, null, 2));
};
const generateServiceReadme = async (serviceDir, serviceName, domain, port) => {
    const readme = `${buildReadmeHeader(serviceName, domain, port)}

${buildReadmeGettingStarted(domain, serviceName)}

${buildReadmeApiInfo(serviceName, port)}`;
    fs.writeFileSync(path.join(serviceDir, 'README.md'), readme);
};
const generateService = async (config) => {
    const { domain, serviceName, port, version } = config;
    const serviceDir = `services/${domain}/${serviceName}`;
    // Create directories
    const dirs = [
        serviceDir,
        `${serviceDir}/src`,
        `${serviceDir}/src/http/Controllers`,
        `${serviceDir}/src/http/Middleware`,
        `${serviceDir}/src/models`,
        `${serviceDir}/src/routes`,
        `${serviceDir}/database/migrations`,
        `${serviceDir}/database/seeders`,
        `${serviceDir}/tests/Feature`,
        `${serviceDir}/tests/Unit`,
    ];
    for (const dir of dirs) {
        if (!fs.existsSync(dir)) {
            fs.mkdirSync(dir, { recursive: true });
        }
    }
    // Generate files
    await generateServiceConfig(serviceDir, serviceName, port, version);
    await generateServiceKernel(serviceDir, serviceName, domain);
    await generateServiceRoutes(serviceDir, serviceName);
    await generateServiceController(serviceDir, serviceName);
    await generateServiceModel(serviceDir, serviceName);
    await generateServiceTest(serviceDir, serviceName);
    await generateServicePackageJson(serviceDir, serviceName, version);
    await generateServiceReadme(serviceDir, serviceName, domain, port);
    await generateDockerfile(serviceDir, serviceName);
    Logger.info(`  ✓ Generated: ${serviceName}`);
};
const generateDockerfile = async (serviceDir, serviceName) => {
    const code = `FROM node:20-alpine

WORKDIR /app

# Install dependencies
# Note: In a real monorepo, you might want to copy the root package.json as well
COPY package.json ./
RUN npm install

# Copy source code
# We assume the context is the root of the project
COPY . .

# Build application
RUN npm run build

ENV NODE_ENV=production
ENV SERVICE_NAME=${serviceName}
ENV SERVICE_PORT=3000

# Standard ZinTrust environment variables
ENV DB_CONNECTION=postgresql
ENV DB_HOST=postgres
ENV DB_PORT=5432
ENV REDIS_HOST=redis
ENV REDIS_PORT=6379

EXPOSE 3000

CMD ["npm", "run", "start"]
`;
    fs.writeFileSync(path.join(serviceDir, 'Dockerfile'), code);
};
const generateSharedUtils = async (domain) => {
    const sharedDir = `services/${domain}/shared`;
    if (!fs.existsSync(sharedDir)) {
        fs.mkdirSync(sharedDir, { recursive: true });
    }
    // Generate shared types
    const typesFile = `${sharedDir}/types.ts`;
    if (!fs.existsSync(typesFile)) {
        const code = `/**
 * Shared types for ${domain} domain
 */

// TODO: Define shared types
`;
        fs.writeFileSync(typesFile, code);
    }
    // Generate shared utils
    const utilsFile = `${sharedDir}/utils.ts`;
    if (!fs.existsSync(utilsFile)) {
        const code = `/**
 * Shared utilities for ${domain} domain
 */

// TODO: Define shared utilities
`;
        fs.writeFileSync(utilsFile, code);
    }
};
const generateDockerCompose = async (domain, services, basePort) => {
    const services_config = services
        .map((service, i) => {
        const port = basePort + i;
        return `  ${service}:
    build:
      context: ../../
      dockerfile: services/${domain}/${service}/Dockerfile
    ports:
      - "${port}:3000"
    environment:
      NODE_ENV: development
      MICROSERVICES: "true"
      SERVICE_NAME: ${service}
      SERVICE_PORT: 3000
      # Database Configuration
      DB_CONNECTION: postgresql
      DB_HOST: postgres
      DB_PORT: 5432
      DB_DATABASE: zintrust_${service}
      DB_USERNAME: zintrust
      DB_PASSWORD: zintrust
      # Cache Configuration
      REDIS_HOST: redis
      REDIS_PORT: 6379
      # Custom environment variables can be added here
    depends_on:
      - postgres
      - redis
`;
    })
        .join('\n');
    const compose = `version: '3.9'

# ZinTrust Microservices Stack: ${domain}
# Run with: docker-compose up -d

services:
${services_config}
  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: zintrust
      POSTGRES_USER: zintrust
      POSTGRES_PASSWORD: zintrust
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"

volumes:
  postgres_data:
`;
    fs.writeFileSync(`services/${domain}/docker-compose.yml`, compose);
};
/**
 * CLI command to generate microservices
 */
export async function generateMicroservices(domain, services, port = 3001) {
    try {
        await MicroserviceGenerator.getInstance().generate({
            domain,
            services: services.map((s) => s.trim()),
            basePort: port,
        });
        Logger.info('✅ Microservices generated successfully!');
    }
    catch (error) {
        Logger.error('❌ Error generating microservices:', error.message);
        process.exit(1);
    }
}
export const generate = async (options) => MicroserviceGenerator.getInstance().generate(options);
export default MicroserviceGenerator;
