const fs = require('fs');
const path = require('path');

class DemoGenerator {  /**
   * Create example files for a module endpoint
   * @param {string} projectName - The project name (e.g., 'belajar')
   * @param {string} endpointName - The endpoint name (e.g., 'contacts')
   * @param {Object} payload - The payload object containing field definitions
   * @param {number} port - The port number for the API
   */
  static createDemoFiles(projectName, endpointName, payload, port = 3000) {
    try {
      console.log(`Creating example files for ${projectName}/${endpointName}...`);

      // 1. Create folder structure: examples/{project}/{endpoint}/{curl,postman,insomnia}
      const { curlPath, postmanPath, insomniaPath } = this.createDemoFolderStructure(projectName, endpointName);

      // 2. Generate dummy data based on payload
      const dummyData = this.generateDummyData(payload);

      // 3. Create JSON payload files in curl/payload/
      this.createJsonPayloadFiles(curlPath, dummyData, payload);

      // 4. Create .bat files based on enabled actions in curl/
      const actions = payload.action || {};

      if (actions.create) {
        this.createDemoCreateFile(curlPath, projectName, endpointName, port);
      }

      if (actions.update) {
        this.createDemoUpdateFile(curlPath, projectName, endpointName, port);
      }

      if (actions.delete) {
        this.createDemoDeleteFile(curlPath, projectName, endpointName, port);
      }

      if (actions.first) {
        this.createDemoFirstFile(curlPath, projectName, endpointName, port);
      }

      if (actions.read) {
        this.createDemoReadFile(curlPath, projectName, endpointName, port);
      }

      if (actions.datatables) {
        this.createDemoDatatablesFile(curlPath, projectName, endpointName, port);
      }

      if (actions.lookup) {
        this.createDemoLookupFile(curlPath, projectName, endpointName, port);
      }

      // Always create master demo file in curl/
      this.createMasterDemoFile(curlPath, endpointName, actions);

      // 5. Create Insomnia collection file in insomnia/
      this.createInsomniaCollection(insomniaPath, projectName, endpointName, dummyData, payload, actions, port);

      // 6. Create Postman collection file in postman/
      this.createPostmanCollection(postmanPath, projectName, endpointName, dummyData, payload, actions, port);

      console.log(`Example files created successfully.`);
      this.printDemoInstructions(curlPath, postmanPath, insomniaPath, projectName, endpointName, port);
    } catch (error) {
      console.error(`Error creating example files: ${error.message}`);
      throw error;
    }
  }

  /**
   * Create example folder structure
   * @param {string} projectName
   * @param {string} endpointName
   * @returns {Object} - Paths: { curlPath, postmanPath, insomniaPath }
   */
  static createDemoFolderStructure(projectName, endpointName) {
    const rootPath = process.cwd();
    const endpointPath = path.join(rootPath, 'examples', projectName, endpointName);
    const curlPath = path.join(endpointPath, 'curl');
    const payloadPath = path.join(curlPath, 'payload');
    const postmanPath = path.join(endpointPath, 'postman');
    const insomniaPath = path.join(endpointPath, 'insomnia');

    // Create folders recursively
    for (const dir of [curlPath, payloadPath, postmanPath, insomniaPath]) {
      if (!fs.existsSync(dir)) {
        fs.mkdirSync(dir, { recursive: true });
      }
    }

    return { curlPath, postmanPath, insomniaPath };
  }

  /**
   * Build field type/constraints map from fieldValidation in payload
   * Fields not declared in fieldValidation are treated as string (fallback to pattern matching)
   * @param {Object} payload
   * @returns {Map} - Map of fieldName -> { type, constraints }
   */
  static buildFieldTypeMap(payload) {
    const typeMap = new Map();

    const validationArray = payload.fieldValidation;
    if (Array.isArray(validationArray)) {
      validationArray.forEach(field => {
        if (field.name) {
          typeMap.set(field.name, {
            type: field.type || 'string',
            constraints: field.constraints || {}
          });
        }
      });
    }

    return typeMap;
  }

  /**
   * Generate sample value based on field type and constraints
   * @param {string} fieldName
   * @param {Object} fieldInfo - { type, constraints }
   * @returns {*} - Sample value or null if no type-based value
   */
  static generateTypedValue(fieldName, fieldInfo) {
    const { type, constraints } = fieldInfo;

    // Auto-generated UUID: show sample UUID value
    if (constraints.autoGenerate && type === 'uuid') {
      return 'a1b2c3d4-e5f6-7890-abcd-ef1234567890';
    }

    // Enum constraint takes highest priority
    if (Array.isArray(constraints.enum) && constraints.enum.length > 0) {
      return constraints.enum[0];
    }

    // Default value from constraints
    if (constraints.default !== undefined) {
      return constraints.default;
    }

    // Type-based sample values
    switch (type) {
      case 'boolean':
        return true;

      case 'integer':
        if (constraints.min !== undefined) return Math.max(constraints.min, 1);
        return 1;

      case 'decimal':
      case 'number': {
        if (constraints.min !== undefined) {
          const val = Math.max(constraints.min, 0) + 100;
          return constraints.precision ? parseFloat(val.toFixed(constraints.precision)) : val;
        }
        return constraints.precision ? parseFloat((100000).toFixed(constraints.precision)) : 100000;
      }

      case 'date':
        if (constraints.format === 'dd/MM/yyyy') {
          const d = new Date();
          return `${String(d.getDate()).padStart(2, '0')}/${String(d.getMonth() + 1).padStart(2, '0')}/${d.getFullYear()}`;
        }
        return new Date().toISOString().split('T')[0];

      case 'datetime':
      case 'timestamp':
        if (constraints.format === 'dd/MM/yyyy HH:mm:ss') {
          const d = new Date();
          return `${String(d.getDate()).padStart(2, '0')}/${String(d.getMonth() + 1).padStart(2, '0')}/${d.getFullYear()} ${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}:${String(d.getSeconds()).padStart(2, '0')}`;
        }
        return new Date().toISOString();

      case 'uuid':
        return 'sample-uuid-replace-with-real-id';

      case 'json':
        return {};

      case 'array':
        return [];

      case 'string':
        // String with format constraint
        if (constraints.format === 'email') return 'demo@example.com';
        if (constraints.format === 'phone') return '+62812345678';
        if (constraints.format === 'url') return 'https://example.com';
        if (constraints.format === 'uuid') return 'sample-uuid-replace-with-real-id';
        // String: return null to fallback to pattern matching
        return null;

      default:
        return null;
    }
  }

  /**
   * Generate sample value based on field name patterns (fallback)
   * @param {string} field - Field name
   * @returns {*} - Sample value
   */
  static generatePatternValue(field) {
    const fieldLower = field.toLowerCase();

    if (fieldLower.includes('email')) return 'demo@example.com';
    if (fieldLower.includes('phone') || fieldLower.includes('telepon') || fieldLower.includes('hp')) return '+62812345678';
    if (fieldLower.includes('first') && fieldLower.includes('name')) return 'John';
    if (fieldLower.includes('last') && fieldLower.includes('name')) return 'Doe';
    if (fieldLower.includes('name') || fieldLower.includes('nama')) return 'Demo Name';
    if (fieldLower.includes('address') || fieldLower.includes('alamat')) return 'Jl. Demo Street No. 123';
    if (fieldLower.includes('city') || fieldLower.includes('kota')) return 'Jakarta';
    if (fieldLower.includes('country') || fieldLower.includes('negara')) return 'Indonesia';
    if (fieldLower.includes('age') || fieldLower.includes('umur')) return 25;
    if (fieldLower.includes('price') || fieldLower.includes('amount') || fieldLower.includes('harga') || fieldLower.includes('jumlah')) return 100000;
    if (fieldLower.includes('date') || fieldLower.includes('tanggal')) return new Date().toISOString().split('T')[0];
    if (fieldLower.includes('time') || fieldLower.includes('waktu')) return new Date().toTimeString().split(' ')[0];
    if (fieldLower.includes('status')) return 'active';
    if (fieldLower.includes('gender') || fieldLower.includes('jenis_kelamin')) return 'L';
    if (fieldLower.includes('company') || fieldLower.includes('perusahaan')) return 'Demo Company';
    if (fieldLower.includes('position') || fieldLower.includes('jabatan')) return 'Manager';
    if (fieldLower.includes('description') || fieldLower.includes('deskripsi') || fieldLower.includes('keterangan')) return 'Demo description text';
    if (fieldLower.includes('url') || fieldLower.includes('website')) return 'https://example.com';
    if (fieldLower.includes('code') || fieldLower.includes('kode')) return 'DMO001';
    if (fieldLower.includes('category') || fieldLower.includes('kategori')) return 'General';

    return `Demo ${field}`;
  }

  /**
   * Generate dummy data based on payload field definitions
   * Reads type info from fieldValidation array
   * Falls back to pattern matching for fields not in fieldValidation
   * @param {Object} payload
   * @returns {Object} - Dummy data object
   */
  static generateDummyData(payload) {
    const dummyData = {};

    if (!payload.fieldName || !Array.isArray(payload.fieldName)) {
      throw new Error('Invalid payload: fieldName array is required');
    }

    // Build type map from fieldValidation
    const typeMap = this.buildFieldTypeMap(payload);

    payload.fieldName.forEach(field => {
      // Skip ID field for add operations
      if (field.toLowerCase() === 'id') {
        return;
      }

      // Priority 1: Type-based generation (from fieldValidation)
      if (typeMap.has(field)) {
        const fieldInfo = typeMap.get(field);
        const typedValue = this.generateTypedValue(field, fieldInfo);

        // undefined = skip field (autoGenerate), null = fallback to pattern
        if (typedValue === undefined) return;
        if (typedValue !== null) {
          dummyData[field] = typedValue;
          return;
        }
      }

      // Priority 2: Pattern-based generation (fallback)
      dummyData[field] = this.generatePatternValue(field);
    });

    return dummyData;
  }

  /**
   * Create demo-create.bat file
   */
  static createDemoCreateFile(curlPath, projectName, endpointName, port) {
    const content = `@echo off
echo Testing CREATE endpoint for ${endpointName}
echo Reading payload from payload/create.json
echo.
type payload\\create.json
echo.

curl -X POST http://localhost:${port}/api/${projectName}/${endpointName}/create ^
  -H "Content-Type: application/json" ^
  -d @payload/create.json

echo.
echo Demo CREATE completed.
pause
`;

    fs.writeFileSync(path.join(curlPath, 'demo-create.bat'), content);
  }

  /**
   * Create demo-update.bat file
   */
  static createDemoUpdateFile(curlPath, projectName, endpointName, port) {
    const content = `@echo off
echo Testing UPDATE endpoint for ${endpointName}
echo Reading payload from payload/update.json
echo.
type payload\\update.json
echo.

curl -X POST http://localhost:${port}/api/${projectName}/${endpointName}/update ^
  -H "Content-Type: application/json" ^
  -d @payload/update.json

echo.
echo Demo UPDATE completed.
pause
`;

    fs.writeFileSync(path.join(curlPath, 'demo-update.bat'), content);
  }

  /**
   * Create demo-delete.bat file
   */
  static createDemoDeleteFile(curlPath, projectName, endpointName, port) {
    const content = `@echo off
echo Testing DELETE endpoint for ${endpointName}
echo Reading payload from payload/delete.json
echo.
type payload\\delete.json
echo.

curl -X POST http://localhost:${port}/api/${projectName}/${endpointName}/delete ^
  -H "Content-Type: application/json" ^
  -d @payload/delete.json

echo.
echo Demo DELETE completed.
pause
`;

    fs.writeFileSync(path.join(curlPath, 'demo-delete.bat'), content);
  }

  /**
   * Create demo-first.bat file
   */
  static createDemoFirstFile(curlPath, projectName, endpointName, port) {
    const content = `@echo off
echo Testing FIRST endpoint for ${endpointName}
echo Reading payload from payload/first.json
echo.
type payload\\first.json
echo.

curl -X POST http://localhost:${port}/api/${projectName}/${endpointName}/first ^
  -H "Content-Type: application/json" ^
  -d @payload/first.json

echo.
echo Demo FIRST completed.
pause
`;

    fs.writeFileSync(path.join(curlPath, 'demo-first.bat'), content);
  }

  /**
   * Create demo-read.bat file
   */
  static createDemoReadFile(curlPath, projectName, endpointName, port) {
    const content = `@echo off
echo Testing READ endpoint for ${endpointName}
echo Reading payload from payload/read.json
echo.
type payload\\read.json
echo.

curl -X POST http://localhost:${port}/api/${projectName}/${endpointName}/read ^
  -H "Content-Type: application/json" ^
  -d @payload/read.json

echo.
echo Demo READ completed.
pause
`;

    fs.writeFileSync(path.join(curlPath, 'demo-read.bat'), content);
  }

  /**
   * Create demo-datatables.bat file for DataTables endpoint
   */
  static createDemoDatatablesFile(curlPath, projectName, endpointName, port) {
    const content = `@echo off
echo Testing DATATABLES endpoint for ${endpointName}
echo Reading payload from payload/datatables.json
echo.
type payload\\datatables.json
echo.

curl -X POST http://localhost:${port}/api/${projectName}/${endpointName}/datatables ^
  -H "Content-Type: application/json" ^
  -d @payload/datatables.json

echo.
echo Demo DATATABLES completed.
pause
`;

    fs.writeFileSync(path.join(curlPath, 'demo-datatables.bat'), content);
  }

  /**
   * Create demo-lookup.bat file
   */
  static createDemoLookupFile(curlPath, projectName, endpointName, port) {
    const content = `@echo off
echo Testing LOOKUP endpoint for ${endpointName}
echo Reading payload from payload/lookup.json
echo.
type payload\\lookup.json
echo.

curl -X POST http://localhost:${port}/api/${projectName}/${endpointName}/lookup ^
  -H "Content-Type: application/json" ^
  -H "x-request-mode: static" ^
  -d @payload/lookup.json

echo.
echo Demo LOOKUP completed.
pause
`;

    fs.writeFileSync(path.join(curlPath, 'demo-lookup.bat'), content);
  }

  /**
   * Create a master demo file that runs all demos
   */
  static createMasterDemoFile(curlPath, endpointName, actions) {
    let content = `@echo off
echo Running all demo files for ${endpointName}
echo.
`;

    let stepNumber = 1;

    if (actions.create) {
      content += `
echo ${stepNumber}. Testing CREATE...
call demo-create.bat
echo.
`;
      stepNumber++;
    }

    if (actions.first) {
      content += `
echo ${stepNumber}. Testing FIRST...
call demo-first.bat
echo.
`;
      stepNumber++;
    }

    if (actions.read) {
      content += `
echo ${stepNumber}. Testing READ...
call demo-read.bat
echo.
`;
      stepNumber++;
    }

    if (actions.lookup) {
      content += `
echo ${stepNumber}. Testing LOOKUP...
call demo-lookup.bat
echo.
`;
      stepNumber++;
    }

    if (actions.datatables) {
      content += `
echo ${stepNumber}. Testing DATATABLES...
call demo-datatables.bat
echo.
`;
      stepNumber++;
    }

    if (actions.update) {
      content += `
echo ${stepNumber}. Testing UPDATE...
call demo-update.bat
echo.
`;
      stepNumber++;
    }

    if (actions.delete) {
      content += `
echo ${stepNumber}. Testing DELETE...
call demo-delete.bat
echo.
`;
      stepNumber++;
    }

    content += `
echo All demos completed!
pause
`;

    fs.writeFileSync(path.join(curlPath, 'run-all-demos.bat'), content);
  }

  /**
   * Create JSON payload files for demo
   * @param {string} curlPath
   * @param {Object} dummyData
   * @param {Object} payload - Original payload for field info
   */
  static createJsonPayloadFiles(curlPath, dummyData, payload) {
    const payloadPath = path.join(curlPath, 'payload');
    const fieldNames = payload.fieldName || [];
    const primaryKeyField = payload.primaryKey || 'id';

    // Get sample value for primary key from dummyData (respects fieldValidation type)
    const primaryKeyValue = dummyData[primaryKeyField] || 'sample-id-replace-with-real-id';

    // Create create.json
    const createData = { ...dummyData };
    fs.writeFileSync(
      path.join(payloadPath, 'create.json'),
      JSON.stringify(createData, null, 2)
    );

    // Create update.json (include primaryKey for identification, modify some values)
    const updateData = { ...dummyData };
    Object.keys(updateData).forEach(key => {
      if (key === primaryKeyField) return; // keep primary key value as-is
      if (key.toLowerCase().includes('name') && !key.toLowerCase().includes('first') && !key.toLowerCase().includes('last')) {
        updateData[key] = 'Updated ' + updateData[key];
      } else if (key.toLowerCase().includes('first')) {
        updateData[key] = 'Jane';
      } else if (key.toLowerCase().includes('email')) {
        updateData[key] = 'updated@example.com';
      }
    });
    fs.writeFileSync(
      path.join(payloadPath, 'update.json'),
      JSON.stringify(updateData, null, 2)
    );

    // Create delete.json (use primaryKey from payload)
    const deleteData = {
      where: [
        {
          key: primaryKeyField,
          value: primaryKeyValue
        }
      ]
    };
    fs.writeFileSync(
      path.join(payloadPath, 'delete.json'),
      JSON.stringify(deleteData, null, 2)
    );

    // Create first.json (use primaryKey from payload)
    const selectFields = payload.fieldName || [primaryKeyField];
    const firstData = {
      where: {
        key: primaryKeyField,
        value: primaryKeyValue
      },
      select: selectFields
    };
    fs.writeFileSync(
      path.join(payloadPath, 'first.json'),
      JSON.stringify(firstData, null, 2)
    );

    // Create read.json (use first searchable field from datatablesWhere)
    const searchByField = (payload.datatablesWhere && payload.datatablesWhere.length > 0 && payload.datatablesWhere[0] !== 'all')
      ? payload.datatablesWhere[0]
      : fieldNames[1] || fieldNames[0] || 'name';
    const readData = {
      page: 1,
      per_page: 10,
      search_value: "",
      search_by: searchByField
    };
    fs.writeFileSync(
      path.join(payloadPath, 'read.json'),
      JSON.stringify(readData, null, 2)
    );

    // Create lookup.json (select first 2 fields from payload)
    const lookupSelectFields = fieldNames.slice(0, 2);
    const lookupData = {
      select: lookupSelectFields
    };
    fs.writeFileSync(
      path.join(payloadPath, 'lookup.json'),
      JSON.stringify(lookupData, null, 2)
    );

    // Create datatables.json
    const datatablesData = {
      draw: 1,
      start: 0,
      length: 10,
      search: {
        value: ""
      }
    };
    fs.writeFileSync(
      path.join(payloadPath, 'datatables.json'),
      JSON.stringify(datatablesData, null, 2)
    );
  }

  /**
   * Create Insomnia collection file
   */
  static createInsomniaCollection(insomniaPath, projectName, endpointName, dummyData, payload, actions, port) {
    const timestamp = Date.now();
    const collectionId = `wrk_${this.generateRandomId()}`;
    const folderId = `fld_${this.generateRandomId()}`;

    const fieldNames = payload.fieldName || [];
    const primaryKeyField = payload.primaryKey || 'id';
    const primaryKeyValue = dummyData[primaryKeyField] || 'sample-id-replace-with-real-id';

    let collection = `type: collection.insomnia.rest/5.0
name: ${projectName.charAt(0).toUpperCase() + projectName.slice(1)}
meta:
  id: ${collectionId}
  created: ${timestamp}
  modified: ${timestamp}
collection:
  - name: ${endpointName}
    meta:
      id: ${folderId}
      created: ${timestamp}
      modified: ${timestamp}
      sortKey: -${timestamp}
    children:`;

    let sortKey = timestamp;

    // Add datatables endpoint
    if (actions.datatables) {
      const reqId = `req_${this.generateRandomId()}`;
      collection += `
      - url: "{{ _.baseUrl }}/api/${projectName}/${endpointName}/datatables"
        name: ${endpointName}/datatables
        meta:
          id: ${reqId}
          created: ${timestamp}
          modified: ${timestamp}
          isPrivate: false
          sortKey: -${sortKey}
        method: POST
        body:
          mimeType: application/json
          text: |-
            {
              "draw": 1,
              "start": 0,
              "length": 10,
              "search": {
                "value": ""
              }
            }
        headers:
          - name: Content-Type
            value: application/json
        settings:
          renderRequestBody: true
          encodeUrl: true
          followRedirects: global
          cookies:
            send: true
            store: true
          rebuildPath: true`;
      sortKey -= 1000;
    }

    // Add lookup endpoint
    if (actions.lookup) {
      const reqId = `req_${this.generateRandomId()}`;
      const lookupSelectFields = fieldNames.slice(0, 2);
      const lookupPayload = JSON.stringify({ select: lookupSelectFields }, null, 2);
      collection += `
      - url: "{{ _.baseUrl }}/api/${projectName}/${endpointName}/lookup"
        name: ${endpointName}/lookup
        meta:
          id: ${reqId}
          created: ${timestamp}
          modified: ${timestamp}
          isPrivate: false
          sortKey: -${sortKey}
        method: POST
        body:
          mimeType: application/json
          text: |-
            ${lookupPayload.split('\n').map(line => '            ' + line).join('\n').trim()}
        headers:
          - name: Content-Type
            value: application/json
          - name: x-request-mode
            value: static
        settings:
          renderRequestBody: true
          encodeUrl: true
          followRedirects: global
          cookies:
            send: true
            store: true
          rebuildPath: true`;
      sortKey -= 1000;
    }

    // Add create endpoint
    if (actions.create) {
      const reqId = `req_${this.generateRandomId()}`;
      const createPayload = JSON.stringify(dummyData, null, 2);
      collection += `
      - url: "{{ _.baseUrl }}/api/${projectName}/${endpointName}/create"
        name: ${endpointName}/create
        meta:
          id: ${reqId}
          created: ${timestamp}
          modified: ${timestamp}
          isPrivate: false
          sortKey: -${sortKey}
        method: POST
        body:
          mimeType: application/json
          text: |-
            ${createPayload.split('\n').map(line => '            ' + line).join('\n').trim()}
        headers:
          - name: Content-Type
            value: application/json
        settings:
          renderRequestBody: true
          encodeUrl: true
          followRedirects: global
          cookies:
            send: true
            store: true
          rebuildPath: true`;
      sortKey -= 1000;
    }

    // Add update endpoint
    if (actions.update) {
      const reqId = `req_${this.generateRandomId()}`;
      const updateData = { ...dummyData };
      Object.keys(updateData).forEach(key => {
        if (key === primaryKeyField) return;
        if (key.toLowerCase().includes('name') && !key.toLowerCase().includes('first') && !key.toLowerCase().includes('last')) {
          updateData[key] = 'Updated ' + updateData[key];
        } else if (key.toLowerCase().includes('first')) {
          updateData[key] = 'Jane';
        } else if (key.toLowerCase().includes('email')) {
          updateData[key] = 'updated@example.com';
        }
      });
      const updatePayload = JSON.stringify(updateData, null, 2);
      collection += `
      - url: "{{ _.baseUrl }}/api/${projectName}/${endpointName}/update"
        name: ${endpointName}/update
        meta:
          id: ${reqId}
          created: ${timestamp}
          modified: ${timestamp}
          isPrivate: false
          sortKey: -${sortKey}
        method: POST
        body:
          mimeType: application/json
          text: |-
            ${updatePayload.split('\n').map(line => '            ' + line).join('\n').trim()}
        headers:
          - name: Content-Type
            value: application/json
        settings:
          renderRequestBody: true
          encodeUrl: true
          followRedirects: global
          cookies:
            send: true
            store: true
          rebuildPath: true`;
      sortKey -= 1000;
    }

    // Add first endpoint
    if (actions.first) {
      const reqId = `req_${this.generateRandomId()}`;
      const selectFields = payload.fieldName || [primaryKeyField];
      const firstPayload = {
        where: {
          key: primaryKeyField,
          value: primaryKeyValue
        },
        select: selectFields
      };
      const firstPayloadStr = JSON.stringify(firstPayload, null, 2);
      collection += `
      - url: "{{ _.baseUrl }}/api/${projectName}/${endpointName}/first"
        name: ${endpointName}/first
        meta:
          id: ${reqId}
          created: ${timestamp}
          modified: ${timestamp}
          isPrivate: false
          sortKey: -${sortKey}
        method: POST
        body:
          mimeType: application/json
          text: |-
            ${firstPayloadStr.split('\n').map(line => '            ' + line).join('\n').trim()}
        headers:
          - name: Content-Type
            value: application/json
        settings:
          renderRequestBody: true
          encodeUrl: true
          followRedirects: global
          cookies:
            send: true
            store: true
          rebuildPath: true`;
      sortKey -= 1000;
    }

    // Add delete endpoint
    if (actions.delete) {
      const reqId = `req_${this.generateRandomId()}`;
      const deletePayload = {
        where: [
          {
            key: primaryKeyField,
            value: primaryKeyValue
          }
        ]
      };
      const deletePayloadStr = JSON.stringify(deletePayload, null, 2);
      collection += `
      - url: "{{ _.baseUrl }}/api/${projectName}/${endpointName}/delete"
        name: ${endpointName}/delete
        meta:
          id: ${reqId}
          created: ${timestamp}
          modified: ${timestamp}
          isPrivate: false
          sortKey: -${sortKey}
        method: POST
        body:
          mimeType: application/json
          text: |-
            ${deletePayloadStr.split('\n').map(line => '            ' + line).join('\n').trim()}
        headers:
          - name: Content-Type
            value: application/json
        settings:
          renderRequestBody: true
          encodeUrl: true
          followRedirects: global
          cookies:
            send: true
            store: true
          rebuildPath: true`;
      sortKey -= 1000;
    }

    // Add read endpoint
    if (actions.read) {
      const reqId = `req_${this.generateRandomId()}`;
      const searchByField = (payload.datatablesWhere && payload.datatablesWhere.length > 0 && payload.datatablesWhere[0] !== 'all')
        ? payload.datatablesWhere[0]
        : fieldNames[1] || fieldNames[0] || 'name';
      const readPayload = JSON.stringify({ page: 1, per_page: 10, search_value: "", search_by: searchByField }, null, 2);
      collection += `
      - url: "{{ _.baseUrl }}/api/${projectName}/${endpointName}/read"
        name: ${endpointName}/read
        meta:
          id: ${reqId}
          created: ${timestamp}
          modified: ${timestamp}
          isPrivate: false
          sortKey: -${sortKey}
        method: POST
        body:
          mimeType: application/json
          text: |-
            ${readPayload.split('\n').map(line => '            ' + line).join('\n').trim()}
        headers:
          - name: Content-Type
            value: application/json
        settings:
          renderRequestBody: true
          encodeUrl: true
          followRedirects: global
          cookies:
            send: true
            store: true
          rebuildPath: true`;
      sortKey -= 1000;
    }

    // Add health endpoint
    const healthReqId = `req_${this.generateRandomId()}`;
    collection += `
      - url: "{{ _.baseUrl }}/api/${projectName}/health"
        name: health
        meta:
          id: ${healthReqId}
          created: ${timestamp}
          modified: ${timestamp}
          isPrivate: false
          sortKey: -${sortKey}
        method: GET
        body:
          mimeType: application/json
        headers:
          - name: Content-Type
            value: application/json
        settings:
          renderRequestBody: true
          encodeUrl: true
          followRedirects: global
          cookies:
            send: true
            store: true
          rebuildPath: true`;

    // Add cookie jar and environments
    const jarId = `jar_${this.generateRandomId()}`;
    const envId = `env_${this.generateRandomId()}`;
    collection += `
cookieJar:
  name: Default Jar
  meta:
    id: ${jarId}
    created: ${timestamp}
    modified: ${timestamp}
environments:
  name: Base Environment
  meta:
    id: ${envId}
    created: ${timestamp}
    modified: ${timestamp}
    isPrivate: false
  data:
    baseUrl: http://localhost:${port}`;

    // Write to insomnia/{endpointName}.yaml
    const insomniaFilePath = path.join(insomniaPath, `${endpointName}.yaml`);
    fs.writeFileSync(insomniaFilePath, collection);
    console.log(`Insomnia collection: ${insomniaFilePath}`);
  }

  /**
   * Create Postman collection file
   */
  static createPostmanCollection(postmanPath, projectName, endpointName, dummyData, payload, actions, port) {
    const collectionId = this.generateRandomId();

    const fieldNames = payload.fieldName || [];
    const primaryKeyField = payload.primaryKey || 'id';
    const primaryKeyValue = dummyData[primaryKeyField] || 'sample-id-replace-with-real-id';

    let collection = {
      info: {
        _postman_id: collectionId,
        name: `${projectName.charAt(0).toUpperCase() + projectName.slice(1)} - ${endpointName.charAt(0).toUpperCase() + endpointName.slice(1)}`,
        schema: "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
        _exporter_id: "restforge-generator"
      },
      item: [],
      variable: [
        {
          key: "baseUrl",
          value: `http://localhost:${port}`,
          type: "string"
        }
      ]
    };

    // Add datatables endpoint
    if (actions.datatables) {
      collection.item.push({
        name: `${endpointName}/datatables`,
        request: {
          method: "POST",
          header: [
            {
              key: "Content-Type",
              value: "application/json",
              type: "text"
            }
          ],
          body: {
            mode: "raw",
            raw: JSON.stringify({
              draw: 1,
              start: 0,
              length: 10,
              search: {
                value: ""
              }
            }, null, 2),
            options: {
              raw: {
                language: "json"
              }
            }
          },
          url: {
            raw: `{{baseUrl}}/api/${projectName}/${endpointName}/datatables`,
            host: ["{{baseUrl}}"],
            path: ["api", projectName, endpointName, "datatables"]
          }
        },
        response: []
      });
    }

    // Add lookup endpoint
    if (actions.lookup) {
      const lookupSelectFields = fieldNames.slice(0, 2);
      collection.item.push({
        name: `${endpointName}/lookup`,
        request: {
          method: "POST",
          header: [
            {
              key: "Content-Type",
              value: "application/json",
              type: "text"
            },
            {
              key: "x-request-mode",
              value: "static",
              type: "text"
            }
          ],
          body: {
            mode: "raw",
            raw: JSON.stringify({
              select: lookupSelectFields
            }, null, 2),
            options: {
              raw: {
                language: "json"
              }
            }
          },
          url: {
            raw: `{{baseUrl}}/api/${projectName}/${endpointName}/lookup`,
            host: ["{{baseUrl}}"],
            path: ["api", projectName, endpointName, "lookup"]
          }
        },
        response: []
      });
    }

    // Add create endpoint
    if (actions.create) {
      collection.item.push({
        name: `${endpointName}/create`,
        request: {
          method: "POST",
          header: [
            {
              key: "Content-Type",
              value: "application/json",
              type: "text"
            }
          ],
          body: {
            mode: "raw",
            raw: JSON.stringify(dummyData, null, 2),
            options: {
              raw: {
                language: "json"
              }
            }
          },
          url: {
            raw: `{{baseUrl}}/api/${projectName}/${endpointName}/create`,
            host: ["{{baseUrl}}"],
            path: ["api", projectName, endpointName, "create"]
          }
        },
        response: []
      });
    }

    // Add update endpoint
    if (actions.update) {
      const updateData = { ...dummyData };
      Object.keys(updateData).forEach(key => {
        if (key === primaryKeyField) return;
        if (key.toLowerCase().includes('name') && !key.toLowerCase().includes('first') && !key.toLowerCase().includes('last')) {
          updateData[key] = 'Updated ' + updateData[key];
        } else if (key.toLowerCase().includes('first')) {
          updateData[key] = 'Jane';
        } else if (key.toLowerCase().includes('email')) {
          updateData[key] = 'updated@example.com';
        }
      });

      collection.item.push({
        name: `${endpointName}/update`,
        request: {
          method: "POST",
          header: [
            {
              key: "Content-Type",
              value: "application/json",
              type: "text"
            }
          ],
          body: {
            mode: "raw",
            raw: JSON.stringify(updateData, null, 2),
            options: {
              raw: {
                language: "json"
              }
            }
          },
          url: {
            raw: `{{baseUrl}}/api/${projectName}/${endpointName}/update`,
            host: ["{{baseUrl}}"],
            path: ["api", projectName, endpointName, "update"]
          }
        },
        response: []
      });
    }

    // Add first endpoint
    if (actions.first) {
      const selectFields = payload.fieldName || [primaryKeyField];
      const firstPayload = {
        where: {
          key: primaryKeyField,
          value: primaryKeyValue
        },
        select: selectFields
      };

      collection.item.push({
        name: `${endpointName}/first`,
        request: {
          method: "POST",
          header: [
            {
              key: "Content-Type",
              value: "application/json",
              type: "text"
            }
          ],
          body: {
            mode: "raw",
            raw: JSON.stringify(firstPayload, null, 2),
            options: {
              raw: {
                language: "json"
              }
            }
          },
          url: {
            raw: `{{baseUrl}}/api/${projectName}/${endpointName}/first`,
            host: ["{{baseUrl}}"],
            path: ["api", projectName, endpointName, "first"]
          }
        },
        response: []
      });
    }

    // Add delete endpoint
    if (actions.delete) {
      const deletePayload = {
        where: [
          {
            key: primaryKeyField,
            value: primaryKeyValue
          }
        ]
      };

      collection.item.push({
        name: `${endpointName}/delete`,
        request: {
          method: "POST",
          header: [
            {
              key: "Content-Type",
              value: "application/json",
              type: "text"
            }
          ],
          body: {
            mode: "raw",
            raw: JSON.stringify(deletePayload, null, 2),
            options: {
              raw: {
                language: "json"
              }
            }
          },
          url: {
            raw: `{{baseUrl}}/api/${projectName}/${endpointName}/delete`,
            host: ["{{baseUrl}}"],
            path: ["api", projectName, endpointName, "delete"]
          }
        },
        response: []
      });
    }

    // Add read endpoint
    if (actions.read) {
      const searchByField = (payload.datatablesWhere && payload.datatablesWhere.length > 0 && payload.datatablesWhere[0] !== 'all')
        ? payload.datatablesWhere[0]
        : fieldNames[1] || fieldNames[0] || 'name';
      const readPayload = {
        page: 1,
        per_page: 10,
        search_value: "",
        search_by: searchByField
      };

      collection.item.push({
        name: `${endpointName}/read`,
        request: {
          method: "POST",
          header: [
            {
              key: "Content-Type",
              value: "application/json",
              type: "text"
            }
          ],
          body: {
            mode: "raw",
            raw: JSON.stringify(readPayload, null, 2),
            options: {
              raw: {
                language: "json"
              }
            }
          },
          url: {
            raw: `{{baseUrl}}/api/${projectName}/${endpointName}/read`,
            host: ["{{baseUrl}}"],
            path: ["api", projectName, endpointName, "read"]
          }
        },
        response: []
      });
    }

    // Add batchCreate endpoint
    if (actions.batchCreate) {
      const rootKey = endpointName.replace(/-/g, '_');
      const isMasterDetail = payload.masterDetail && payload.masterDetail.enabled;
      const detailTable = isMasterDetail ? payload.masterDetail.detailTable : null;
      const detailTableName = detailTable ? detailTable.split('.').pop() : 'items';

      let batchCreatePayload = {};
      batchCreatePayload[rootKey] = { ...dummyData };

      // Add detail items if master-detail
      if (isMasterDetail && detailTableName) {
        batchCreatePayload[rootKey][detailTableName] = [
          {
            line_number: 1,
            item_id: "item-uuid-1",
            qty_received: 10,
            uom: "PCS",
            unit_price: 100000,
            notes: "Demo item 1"
          },
          {
            line_number: 2,
            item_id: "item-uuid-2",
            qty_received: 20,
            uom: "PCS",
            unit_price: 50000,
            notes: "Demo item 2"
          }
        ];
      }

      collection.item.push({
        name: `${endpointName}/batchcreate`,
        request: {
          method: "POST",
          header: [
            {
              key: "Content-Type",
              value: "application/json",
              type: "text"
            }
          ],
          body: {
            mode: "raw",
            raw: JSON.stringify(batchCreatePayload, null, 2),
            options: {
              raw: {
                language: "json"
              }
            }
          },
          url: {
            raw: `{{baseUrl}}/api/${projectName}/${endpointName}/batchcreate`,
            host: ["{{baseUrl}}"],
            path: ["api", projectName, endpointName, "batchcreate"]
          }
        },
        response: []
      });
    }

    // Add batchUpdate endpoint
    if (actions.batchUpdate) {
      const rootKey = endpointName.replace(/-/g, '_');
      const isMasterDetail = payload.masterDetail && payload.masterDetail.enabled;
      const detailTable = isMasterDetail ? payload.masterDetail.detailTable : null;
      const detailTableName = detailTable ? detailTable.split('.').pop() : 'items';
      const primaryKey = payload.primaryKey || 'id';

      let batchUpdatePayload = {};
      batchUpdatePayload[rootKey] = {
        ...dummyData,
        [primaryKey]: "existing-id-to-update"
      };

      // Add detail items if master-detail using insert/update/delete pattern
      if (isMasterDetail && detailTableName) {
        const detailPrimaryKey = payload.masterDetail?.detailConfig?.primaryKey || `${detailTableName}_id`;
        batchUpdatePayload[rootKey][detailTableName] = {
          delete: [
            {
              [detailPrimaryKey]: "item-id-to-delete"
            }
          ],
          update: [
            {
              [detailPrimaryKey]: "existing-item-id-1",
              line_number: 1,
              item_id: "item-uuid-1",
              qty_received: 12,
              uom: "PCS",
              unit_price: 100000,
              notes: "Updated item 1"
            },
            {
              [detailPrimaryKey]: "existing-item-id-2",
              line_number: 2,
              item_id: "item-uuid-2",
              qty_received: 25,
              uom: "PCS",
              unit_price: 50000,
              notes: "Updated item 2"
            }
          ],
          insert: [
            {
              line_number: 3,
              item_id: "item-uuid-3",
              qty_received: 15,
              uom: "PCS",
              unit_price: 75000,
              notes: "New item 3"
            }
          ]
        };
      }

      collection.item.push({
        name: `${endpointName}/batchupdate`,
        request: {
          method: "POST",
          header: [
            {
              key: "Content-Type",
              value: "application/json",
              type: "text"
            }
          ],
          body: {
            mode: "raw",
            raw: JSON.stringify(batchUpdatePayload, null, 2),
            options: {
              raw: {
                language: "json"
              }
            }
          },
          url: {
            raw: `{{baseUrl}}/api/${projectName}/${endpointName}/batchupdate`,
            host: ["{{baseUrl}}"],
            path: ["api", projectName, endpointName, "batchupdate"]
          }
        },
        response: []
      });
    }

    // Add health endpoint
    collection.item.push({
      name: "health",
      request: {
        method: "GET",
        header: [
          {
            key: "Content-Type",
            value: "application/json",
            type: "text"
          }
        ],
        url: {
          raw: `{{baseUrl}}/api/${projectName}/health`,
          host: ["{{baseUrl}}"],
          path: ["api", projectName, "health"]
        }
      },
      response: []
    });

    // Write to postman/{endpointName}.json
    const postmanFilePath = path.join(postmanPath, `${endpointName}.json`);
    fs.writeFileSync(postmanFilePath, JSON.stringify(collection, null, 2));
    console.log(`Postman collection: ${postmanFilePath}`);
  }

  /**
   * Generate random ID for Insomnia
   */
  static generateRandomId() {
    return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
  }

  /**
   * Print instructions for using example files
   */
  static printDemoInstructions(curlPath, postmanPath, insomniaPath, projectName, endpointName, port) {
    const batFiles = fs.readdirSync(curlPath).filter(f => f.endsWith('.bat'));
    const postmanFiles = fs.readdirSync(postmanPath).filter(f => f.endsWith('.json'));
    const insomniaFiles = fs.readdirSync(insomniaPath).filter(f => f.endsWith('.yaml'));

    console.log(`
EXAMPLE FILES INSTRUCTIONS:
1. Make sure the server is running with: npx restforge serve --project=${projectName} --port=${port}
2. Open curl folder: ${curlPath}
3. Run individual demo files or run-all-demos.bat

curl files:
${batFiles.map(f => `   - ${path.join(curlPath, f)}`).join('\n')}

Postman collection:
${postmanFiles.map(f => `   - ${path.join(postmanPath, f)}`).join('\n')}

Insomnia collection:
${insomniaFiles.map(f => `   - ${path.join(insomniaPath, f)}`).join('\n')}
`);
  }
}

module.exports = DemoGenerator;
