const pool = require('../config/db');
const asyncHandler = require('../middlewares/asyncHandler');
const {
  getAllSubmissions,
  getSubmissionsByAuditId,
  getSubmissionsByAuditIdWithMapping,
  getSubmissionById,
  getMediaByAnswerAndOccurrence,
  isAnswerInSubmission,
  createSubmission,
  updateSubmission,
  deleteSubmission
} = require('../repositories/submissionsRepository');
const { canUserAddObservations, updateAuditStatusBasedOnForms } = require('../repositories/auditsRepository');
const { insertOpening, upsertMapping, deleteByClosing, getFirstOpenOpeningByAuditId } = require('../repositories/openingClosingMapRepository');
const { insertAuditLog } = require('../repositories/auditLogsRepository');

// Returns true if form is in sat_archive_form (parent forms cannot be added to sat_audits)
const isFormArchived = async (formId) => {
  if (!formId) return false;
  const [rows] = await pool.query('SELECT 1 FROM sat_archive_form WHERE form_id = ?', [formId]);
  return rows.length > 0;
};

// Get form_type_id for a form (1 = Opening, 2 = Closing)
const getFormTypeId = async (formId) => {
  if (!formId) return null;
  const [rows] = await pool.query('SELECT form_type_id FROM sat_form_details WHERE form_id = ?', [formId]);
  return rows[0]?.form_type_id ?? null;
};

// Get all sat_submissions (optional query: ?audit_id= to filter by audit; when audit_id present, includes opening-closing mapping fields)
exports.getAllSubmissions = asyncHandler(async (req, res) => {
    const auditId = req.query.audit_id;
    const rows = auditId ? await getSubmissionsByAuditIdWithMapping(auditId) : await getAllSubmissions();
    res.status(200).json({ success: true, data: rows });
});

// Get submission by ID
exports.getSubmissionById = asyncHandler(async (req, res) => {
    const { id } = req.params;
    const submission = await getSubmissionById(id);
    
    if (!submission) {
        return res.status(404).json({ success: false, message: 'Submission not found' });
    }
    
    res.status(200).json({ success: true, data: submission });
});

// Get submission media as binary (image/video) - avoids data URL in JSON and ERR_INVALID_URL
exports.getSubmissionMedia = asyncHandler(async (req, res) => {
    const { id: submissionId, answersId, occurrence } = req.params;
    const belongs = await isAnswerInSubmission(submissionId, answersId);
    if (!belongs) {
        return res.status(404).json({ success: false, message: 'Media not found' });
    }
    const result = await getMediaByAnswerAndOccurrence(answersId, occurrence);
    if (!result) {
        return res.status(404).json({ success: false, message: 'Media not found' });
    }
    res.setHeader('Content-Type', result.contentType);
    res.send(result.buffer);
});

// Create submission
exports.createSubmission = asyncHandler(async (req, res) => {
    const userId = req.user?.userId;
    if (!userId) {
        return res.status(401).json({ success: false, message: 'Access denied. User not authenticated.' });
    }

    const { audit_id, submitter_id, form_id, opening_sub_id, sat_answers } = req.body;
    
    if (!audit_id || !submitter_id || !form_id) {
        return res.status(400).json({ success: false, message: 'audit_id, submitter_id, and form_id are required' });
    }

    if (Number(submitter_id) !== Number(userId)) {
        return res.status(403).json({ success: false, message: 'You can only submit as yourself.' });
    }

    if (await isFormArchived(form_id)) {
        return res.status(400).json({ success: false, message: 'Parent (archived) forms cannot be added to sat_audits. Please select a non-archived form.' });
    }

    const formTypeId = await getFormTypeId(form_id);
    if (formTypeId == null) {
        return res.status(400).json({ success: false, message: 'Form not found.' });
    }
    // Opening Form = 1: only auditor or internal co-auditors can add observations
    if (Number(formTypeId) === 1) {
        const allowed = await canUserAddObservations(audit_id, userId);
        if (!allowed) {
            return res.status(403).json({
                success: false,
                message: 'Only the auditor or internal co-auditors can add observations to this audit.'
            });
        }
    }
    
    // Determine submission_type_id based on form_type_id
    // form_type_id 1 (Opening form) -> submission_type_id 1 (Open_Observations)
    // form_type_id 2 (Closing form) -> submission_type_id 2 (Close_Observations)
    const submission_type_id = Number(formTypeId);
    
    // When submitting a Closing form, optionally link to an opening observation (opening_sub_id)
    if (opening_sub_id != null && Number(formTypeId) === 2) {
        const [openRows] = await pool.query(
            'SELECT s.submission_id, s.audit_id FROM sat_submissions s INNER JOIN sat_form_details f ON s.form_id = f.form_id WHERE s.submission_id = ? AND s.audit_id = ? AND f.form_type_id = 1',
            [opening_sub_id, audit_id]
        );
        if (openRows.length === 0) {
            return res.status(400).json({ success: false, message: 'opening_sub_id must be a submission of an Opening form in the same audit.' });
        }
    }

    const connection = await pool.getConnection();
    try {
        await connection.beginTransaction();

        const submissionData = { audit_id, submitter_id, form_id, submission_type_id };
        const submissionId = await createSubmission(submissionData, sat_answers, connection, true);

        // Sub-API: update sat_opening_closing_map based on form_type_id (after submission is created)
        const formType = Number(formTypeId);
        if (formType === 1) {
            // Opening form: insert audit_id and submission_id into sat_opening_closing_map (audit_id, opening_sub_id, closing_sub_id=NULL)
            await insertOpening(Number(audit_id), submissionId, connection);
        } else if (formType === 2) {
            // Closing form: match audit_id and set this submission_id in closing_sub_id for the corresponding opening
            let openingSubIdToUse = opening_sub_id != null ? Number(opening_sub_id) : null;
            if (openingSubIdToUse == null) {
                const firstOpen = await getFirstOpenOpeningByAuditId(audit_id, connection);
                if (firstOpen) openingSubIdToUse = firstOpen.opening_sub_id;
            }
            if (openingSubIdToUse != null) {
                await upsertMapping(Number(audit_id), openingSubIdToUse, submissionId, connection);
            }
        }

        await updateAuditStatusBasedOnForms(audit_id, connection);

        await insertAuditLog(audit_id, userId, 'OBSERVATION_ADDED', {}, connection);

        await connection.commit();
        res.status(201).json({ success: true, message: 'Submission created successfully', data: { submission_id: submissionId } });
    } catch (error) {
        await connection.rollback();
        throw error;
    } finally {
        connection.release();
    }
});

// Update submission
exports.updateSubmission = asyncHandler(async (req, res) => {
    const { id } = req.params;
    const { audit_id, submitter_id, form_id, opening_sub_id, sat_answers } = req.body;
    
    if (form_id !== undefined && (await isFormArchived(form_id))) {
        return res.status(400).json({ success: false, message: 'Parent (archived) forms cannot be added to sat_audits.' });
    }
    
    const existing = await getSubmissionById(id);
    if (!existing) {
        return res.status(404).json({ success: false, message: 'Submission not found' });
    }
    const currentAuditId = existing.audit_id;
    const currentFormId = form_id !== undefined ? form_id : existing.form_id;
    const formTypeId = await getFormTypeId(currentFormId);
    
    if (opening_sub_id !== undefined && formTypeId === 2) {
        if (opening_sub_id != null) {
            const [openRows] = await pool.query(
                'SELECT s.submission_id FROM sat_submissions s INNER JOIN sat_form_details f ON s.form_id = f.form_id WHERE s.submission_id = ? AND s.audit_id = ? AND f.form_type_id = 1',
                [opening_sub_id, currentAuditId]
            );
            if (openRows.length === 0) {
                return res.status(400).json({ success: false, message: 'opening_sub_id must be a submission of an Opening form in the same audit.' });
            }
        }
    }
    
    const updateData = {};
    if (audit_id !== undefined) updateData.audit_id = audit_id;
    if (submitter_id !== undefined) updateData.submitter_id = submitter_id;
    if (form_id !== undefined) {
        updateData.form_id = form_id;
        // If form_id is being updated, also update submission_type_id
        const newFormTypeId = await getFormTypeId(form_id);
        if (newFormTypeId !== null) {
            updateData.submission_type_id = Number(newFormTypeId);
        }
    }
    
    const connection = await pool.getConnection();
    try {
        await connection.beginTransaction();
        
        const updated = await updateSubmission(id, updateData, sat_answers, connection);
        
        if (!updated) {
            await connection.rollback();
            return res.status(404).json({ success: false, message: 'Submission not found' });
        }
        
        if (formTypeId === 2) {
            if (opening_sub_id !== undefined) {
                if (opening_sub_id != null) {
                    await upsertMapping(currentAuditId, opening_sub_id, parseInt(id, 10), connection);
                } else {
                    await deleteByClosing(currentAuditId, parseInt(id, 10), connection);
                }
            }
        }
        
        await connection.commit();
        res.status(200).json({ success: true, message: 'Submission updated successfully' });
    } catch (error) {
        await connection.rollback();
        throw error;
    } finally {
        connection.release();
    }
});

// Delete submission
exports.deleteSubmission = asyncHandler(async (req, res) => {
    const { id } = req.params;
    const connection = await pool.getConnection();
    try {
        await connection.beginTransaction();
        
        const deleted = await deleteSubmission(id, connection);
        
        if (!deleted) {
            await connection.rollback();
            return res.status(404).json({ success: false, message: 'Submission not found' });
        }
        
        await connection.commit();
        res.status(200).json({ success: true, message: 'Submission deleted successfully' });
    } catch (error) {
        await connection.rollback();
        throw error;
    } finally {
        connection.release();
    }
});

