const db = require('../db/models');
const Mock_trialsDBApi = require('../db/api/mock_trials');
const Mock_trial_turnsDBApi = require('../db/api/mock_trial_turns'); // Import Turns API
const { LocalAIApi } = require('../ai/LocalAIApi'); // Import AI
const processFile = require("../middlewares/upload");
const ValidationError = require('./notifications/errors/validation');
const csv = require('csv-parser');
const axios = require('axios');
const config = require('../config');
const stream = require('stream');





module.exports = class Mock_trialsService {
  static async create(data, currentUser) {
    const transaction = await db.sequelize.transaction();
    try {
      await Mock_trialsDBApi.create(
        data,
        {
          currentUser,
          transaction,
        },
      );

      await transaction.commit();
    } catch (error) {
      await transaction.rollback();
      throw error;
    }
  };

  static async generateTurn(id, currentUser) {
    const transaction = await db.sequelize.transaction();
    
    try {
      // 1. Fetch Mock Trial with Roles and Turns
      const mockTrial = await db.mock_trials.findOne({
          where: { id },
          include: [
              { model: db.mock_trial_roles, as: 'mock_trial_roles_mock_trial' },
              { model: db.mock_trial_turns, as: 'mock_trial_turns_mock_trial', limit: 10, order: [['spoken_at', 'DESC']] }
          ],
          transaction
      });

      if (!mockTrial) throw new ValidationError('mock_trialsNotFound');

      const roles = mockTrial.mock_trial_roles_mock_trial;
      const recentTurns = (mockTrial.mock_trial_turns_mock_trial || []).reverse(); 

      // 2. Construct Prompt
      const rolesDesc = roles.map(r => `${r.role_name} (${r.display_name})`).join(', ');
      const history = recentTurns.map(t => {
          const role = roles.find(r => r.id === t.roleId);
          return `${role ? role.role_name : 'Unknown'}: ${t.content}`;
      }).join('\n');

      const prompt = `Simulate the next turn in a mock trial.
      Scenario: ${mockTrial.scenario_brief || 'Standard civil trial'}.
      Roles: ${rolesDesc}.
      
      Recent History:
      ${history}
      
      Instructions:
      - Identify who should speak next based on standard courtroom procedure.
      - Generate their dialogue.
      - Return JSON: { "role_name": "...", "content": "...", "turn_type": "..." }
      - turn_type options: opening, direct_exam, cross_exam, argument, ruling, objection, sidebar, closing, general.
      `;

      // 3. Call AI
      const aiResponse = await LocalAIApi.createResponse({
        input: [
            { role: 'system', content: 'You are a legal simulator. Return JSON.' },
            { role: 'user', content: prompt }
        ]
      });

      if (!aiResponse.success) throw new Error("AI Generation Failed");

      let role_name, content, turn_type;
      try {
        const decoded = LocalAIApi.decodeJsonFromResponse(aiResponse);
        role_name = decoded.role_name;
        content = decoded.content;
        turn_type = decoded.turn_type;
      } catch (e) {
          // Fallback if AI fails to return strict JSON
          content = LocalAIApi.extractText(aiResponse);
          role_name = roles[0] ? roles[0].role_name : 'Unknown';
          turn_type = 'general';
      }

      // Find role ID
      const speakerRole = roles.find(r => r.role_name === role_name) || roles[0]; 

      if (!speakerRole) {
          throw new Error("No roles defined for this mock trial. Cannot generate turn.");
      }

      // 4. Save Turn
      const newTurn = await Mock_trial_turnsDBApi.create({
          mock_trialId: id,
          roleId: speakerRole.id,
          content,
          turn_type: turn_type || 'general',
          spoken_at: new Date()
      }, { currentUser, transaction });
      
      // Need to reload to get associations if needed, but for now return plain
      // Actually, let's fetch the role to return complete data
      newTurn.role = speakerRole;

      await transaction.commit();
      return newTurn;

    } catch (error) {
        await transaction.rollback();
        console.error("Generate turn error", error);
        throw error;
    }
  }

  static async bulkImport(req, res, sendInvitationEmails = true, host) {
    const transaction = await db.sequelize.transaction();

    try {
      await processFile(req, res);
      const bufferStream = new stream.PassThrough();
      const results = [];

      await bufferStream.end(Buffer.from(req.file.buffer, "utf-8")); // convert Buffer to Stream

      await new Promise((resolve, reject) => {
        bufferStream
          .pipe(csv())
          .on('data', (data) => results.push(data))
          .on('end', async () => {
            console.log('CSV results', results);
            resolve();
          })
          .on('error', (error) => reject(error));
      })

      await Mock_trialsDBApi.bulkImport(results, {
          transaction,
          ignoreDuplicates: true,
          validate: true,
          currentUser: req.currentUser
      });

      await transaction.commit();
    } catch (error) {
      await transaction.rollback();
      throw error;
    }
  }

  static async update(data, id, currentUser) {
    const transaction = await db.sequelize.transaction();
    try {
      let mock_trials = await Mock_trialsDBApi.findBy(
        {id},
        {transaction},
      );

      if (!mock_trials) {
        throw new ValidationError(
          'mock_trialsNotFound',
        );
      }

      const updatedMock_trials = await Mock_trialsDBApi.update(
        id,
        data,
        {
          currentUser,
          transaction,
        },
      );

      await transaction.commit();
      return updatedMock_trials;

    } catch (error) {
      await transaction.rollback();
      throw error;
    }
  };

  static async deleteByIds(ids, currentUser) {
    const transaction = await db.sequelize.transaction();

    try {
      await Mock_trialsDBApi.deleteByIds(ids, {
        currentUser,
        transaction,
      });

      await transaction.commit();
    } catch (error) {
      await transaction.rollback();
      throw error;
    }
  }

  static async remove(id, currentUser) {
    const transaction = await db.sequelize.transaction();

    try {
      await Mock_trialsDBApi.remove(
        id,
        {
          currentUser,
          transaction,
        },
      );

      await transaction.commit();
    } catch (error) {
      await transaction.rollback();
      throw error;
    }
  }

  
};