<?php
namespace Modules\Projects\Controllers;

use Core\Controller;
use Core\Auth;
use Modules\Projects\Models\ProjectModel;
use Modules\Projects\Models\FinancialAnalysisModel;

class ProjectController extends Controller
{
    protected $projectModel;
    protected $financeModel;

    public function __construct()
    {
        parent::__construct();
        $this->projectModel = new ProjectModel();
        $this->financeModel = new FinancialAnalysisModel();
    }

    public function index(): void
    {
        $this->requireAuth();
        $orgId = $this->scopedOrgId();
        $filters = [
            'status' => sanitize($this->getGet('status', '')),
            'category' => sanitize($this->getGet('category', '')),
            'client_id' => sanitize($this->getGet('client_id', '')),
            'search' => sanitize($this->getGet('search', ''))
        ];

        $page = max(1, (int) $this->getGet('page', 1));
        $perPage = 12;

        $projects = $this->projectModel->getAllForOrg($orgId, $filters, $page, $perPage);
        $total = $this->projectModel->countForOrg($orgId, $filters);
        $stats = $this->projectModel->getProjectStats($orgId);

        $this->render('projects/index', [
            'pageTitle' => 'Projects',
            'projects' => $projects,
            'filters' => $filters,
            'stats' => $stats,
            'isPlatformView' => $orgId === null,
            'page' => $page,
            'paginationData' => [
                'current_page' => $page,
                'total_pages' => (int)ceil($total / $perPage),
                'total_items' => $total,
                'url_pattern' => APP_URL . '/projects?' . http_build_query(array_filter($filters)) . '&page=',
            ],
        ]);
    }

    public function create(): void
    {
        $this->requireAuth();
        $orgId = Auth::orgId();
        if ($orgId === null) {
            $this->setFlash('error', 'Projects belong to an organization. Log in as an organization user to create one.');
            $this->redirect(APP_URL . '/projects');
        }
        $categories = $this->dbQuery("SELECT * FROM project_categories WHERE org_id = ? AND is_active = 1 ORDER BY name", [$orgId]);
        $clients = $this->dbQuery("SELECT * FROM clients WHERE org_id = ? AND is_active = 1 ORDER BY company_name", [$orgId]);
        $managers = $this->dbQuery("SELECT * FROM users WHERE org_id = ? AND is_active = 1 ORDER BY first_name", [$orgId]);
        $contractors = $this->dbQuery("SELECT * FROM contractors WHERE org_id = ? AND is_active = 1 AND is_approved = 1 ORDER BY company_name", [$orgId]);
        $templates = $this->dbQuery("SELECT id, name FROM report_templates WHERE org_id = ? AND is_active = 1 ORDER BY name", [$orgId]);

        $this->render('projects/create', [
            'pageTitle' => 'Create New Project',
            'categories' => $categories,
            'clients' => $clients,
            'managers' => $managers,
            'contractors' => $contractors,
            'templates' => $templates,
        ]);
    }

    public function store(): void
    {
        $this->requireAuth();
        if (!$this->validateCsrf()) {
            $this->setFlash('error', 'Invalid security token.');
            $this->redirect(APP_URL . '/projects/create');
        }

        $orgId = Auth::orgId();
        if ($orgId === null) {
            $this->setFlash('error', 'Projects belong to an organization. Log in as an organization user to create one.');
            $this->redirect(APP_URL . '/projects');
        }
        $name = sanitize($this->getPost('name', ''));
        $clientId = (int) $this->getPost('client_id', 0);

        if (empty($name) || empty($clientId)) {
            $this->setFlash('error', 'Project Name and Client are required.');
            $this->redirect(APP_URL . '/projects/create');
        }

        $projectCode = generateCode('PRJ', 6);

        $data = [
            'org_id' => $orgId,
            'project_code' => $projectCode,
            'name' => $name,
            'category_id' => (int) $this->getPost('category_id') ?: null,
            'client_id' => $clientId,
            'description' => sanitize($this->getPost('description', '')),
            'location' => sanitize($this->getPost('location', '')),
            'currency' => $this->getPost('currency', DEFAULT_CURRENCY),
            'project_value' => (float) $this->getPost('project_value', 0),
            'start_date' => $this->getPost('start_date') ?: null,
            'expected_end_date' => $this->getPost('expected_end_date') ?: null,
            'implementing_vendor_type' => $this->getPost('implementing_vendor_type', 'in_house'),
            'report_template_id' => (int) $this->getPost('report_template_id') ?: null,
            'status' => 'active',
            'created_by' => Auth::id(),
        ];

        $projectId = $this->projectModel->create($data);

        if (!empty($_FILES['purchase_order']['name'])) {
            $upload = $this->uploadFile($_FILES['purchase_order'], 'projects/purchase_orders');
            if ($upload) {
                $this->saveProjectDocument($projectId, 'purchase_order', $upload, $_FILES['purchase_order']['type']);
            }
        }

        if (!empty($_FILES['financial_analysis']['name'])) {
            $upload = $this->uploadFile($_FILES['financial_analysis'], 'projects/financial_analysis', ['xlsx', 'xls']);
            if ($upload) {
                $this->saveProjectDocument($projectId, 'financial_analysis', $upload, $_FILES['financial_analysis']['type']);
                $this->financeModel->parseExcelDocument(UPLOAD_PATH . '/' . $upload['path'], $projectId, Auth::id());
            }
        }

        if (!empty($_FILES['supporting_documents']) && !empty($_FILES['supporting_documents']['name'][0])) {
            $count = count($_FILES['supporting_documents']['name']);
            for ($i = 0; $i < $count; $i++) {
                if ($_FILES['supporting_documents']['error'][$i] !== UPLOAD_ERR_OK) {
                    continue;
                }
                $fileInput = [
                    'name' => $_FILES['supporting_documents']['name'][$i],
                    'type' => $_FILES['supporting_documents']['type'][$i],
                    'tmp_name' => $_FILES['supporting_documents']['tmp_name'][$i],
                    'error' => $_FILES['supporting_documents']['error'][$i],
                    'size' => $_FILES['supporting_documents']['size'][$i],
                ];
                $upload = $this->uploadFile($fileInput, 'projects/supporting');
                if ($upload) {
                    $this->saveProjectDocument($projectId, 'supporting', $upload, $fileInput['type']);
                }
            }
        }

        $vendorType = $data['implementing_vendor_type'];
        if ($vendorType === 'in_house') {
            $pmId = (int) $this->getPost('project_manager_id', 0);
            if ($pmId) {
                $this->projectModel->assignProjectManager($projectId, $pmId, Auth::id());
                $this->notifyProjectManager($projectId, $pmId);
            }
        } else {
            $contractorIds = $_POST['contractors'] ?? [];
            foreach ($contractorIds as $contractorId) {
                $contractorId = (int) $contractorId;
                if (!$contractorId) {
                    continue;
                }
                $this->projectModel->assignContractor($projectId, $contractorId, Auth::id());
                $this->notifyContractorAssignment($projectId, $contractorId);

                if ($data['project_value'] > 0) {
                    $chargeAmount = $data['project_value'] * PLATFORM_FEE_RATE;
                    $refNum = generateCode('SUB', 8);
                    $this->dbExecute(
                        "INSERT INTO subscription_charges (org_id, project_id, contractor_id, charge_type, reference_number, po_amount, charge_rate, charge_amount, currency, status, created_by, created_at) VALUES (?, ?, ?, 'platform_fee', ?, ?, ?, ?, ?, 'pending', ?, NOW())",
                        [$orgId, $projectId, $contractorId, $refNum, $data['project_value'], PLATFORM_FEE_RATE, $chargeAmount, $data['currency'], Auth::id()]
                    );
                }
            }
        }

        logAudit('create_project', 'projects', 'project', $projectId, null, $data);
        $this->setFlash('success', 'Project created successfully.');
        $this->redirect(APP_URL . "/projects/{$projectId}");
    }

    public function show(int $id): void
    {
        $this->requireAuth();
        $project = $this->projectModel->getWithDetails($id);
        if (!$project || $project['org_id'] != Auth::orgId()) {
            http_response_code(404);
            $this->render('shared/404');
            return;
        }

        $finance = $this->financeModel->getAnalysis($id);
        $profitability = $this->financeModel->calculateProfitability($id);

        $this->render('projects/show', [
            'pageTitle' => $project['name'],
            'project' => $project,
            'finance' => $finance,
            'profitability' => $profitability
        ]);
    }

    public function edit(int $id): void
    {
        $this->requireAuth();
        $project = $this->projectModel->getWithDetails($id);
        if (!$project || $project['org_id'] != Auth::orgId()) {
            http_response_code(404);
            $this->render('shared/404');
            return;
        }

        $orgId = Auth::orgId();
        $categories = $this->dbQuery("SELECT * FROM project_categories WHERE org_id = ? ORDER BY name", [$orgId]);
        $clients = $this->dbQuery("SELECT * FROM clients WHERE org_id = ? ORDER BY company_name", [$orgId]);
        $managers = $this->dbQuery("SELECT * FROM users WHERE org_id = ? AND is_active = 1 ORDER BY first_name", [$orgId]);
        $contractors = $this->dbQuery("SELECT * FROM contractors WHERE org_id = ? AND is_active = 1 AND is_approved = 1 ORDER BY company_name", [$orgId]);
        $templates = $this->dbQuery("SELECT id, name FROM report_templates WHERE org_id = ? AND is_active = 1 ORDER BY name", [$orgId]);

        $this->render('projects/create', [
            'pageTitle' => 'Edit Project',
            'project' => $project,
            'categories' => $categories,
            'clients' => $clients,
            'managers' => $managers,
            'contractors' => $contractors,
            'templates' => $templates,
        ]);
    }

    public function update(int $id): void
    {
        $this->requireAuth();
        if (!$this->validateCsrf()) {
            $this->setFlash('error', 'Invalid token.');
            $this->redirect(APP_URL . "/projects/{$id}/edit");
        }

        $existing = $this->projectModel->find($id);
        if (!$existing || (int)$existing['org_id'] !== (int)Auth::orgId()) {
            $this->setFlash('error', 'Project not found.');
            $this->redirect(APP_URL . '/projects');
        }

        $data = [
            'name' => sanitize($this->getPost('name', '')),
            'category_id' => (int) $this->getPost('category_id') ?: null,
            'client_id' => (int) $this->getPost('client_id', 0),
            'description' => sanitize($this->getPost('description', '')),
            'location' => sanitize($this->getPost('location', '')),
            'project_value' => (float) $this->getPost('project_value', 0),
            'start_date' => $this->getPost('start_date') ?: null,
            'expected_end_date' => $this->getPost('expected_end_date') ?: null,
            'status' => $this->getPost('status', $existing['status']),
            'updated_by' => Auth::id(),
        ];

        $this->projectModel->update($id, $data);
        logAudit('update_project', 'projects', 'project', $id, $existing, $data);
        $this->setFlash('success', 'Project updated.');
        $this->redirect(APP_URL . "/projects/{$id}");
    }

    public function delete(int $id): void
    {
        $this->requireAuth();
        $existing = $this->projectModel->find($id);
        if (!$existing || (int)$existing['org_id'] !== (int)Auth::orgId()) {
            $this->setFlash('error', 'Project not found.');
            $this->redirect(APP_URL . '/projects');
        }
        $this->projectModel->update($id, ['status' => 'cancelled']);
        logAudit('cancel_project', 'projects', 'project', $id);
        $this->setFlash('success', 'Project cancelled.');
        $this->redirect(APP_URL . "/projects");
    }

    public function categories(): void
    {
        $this->requireAuth();
        $orgId = Auth::orgId();
        $categories = $this->dbQuery(
            "SELECT pc.*, COUNT(p.id) as project_count FROM project_categories pc LEFT JOIN projects p ON pc.id = p.category_id WHERE pc.org_id = ? GROUP BY pc.id ORDER BY pc.name ASC",
            [$orgId]
        );

        $this->render('projects/categories', [
            'pageTitle' => 'Project Categories',
            'categories' => $categories
        ]);
    }

    public function createCategory(): void
    {
        $this->requireAuth();
        if (!$this->validateCsrf()) {
            $this->redirect(APP_URL . '/projects/categories');
        }

        $name = sanitize($this->getPost('name', ''));
        if (!empty($name)) {
            $this->dbExecute(
                "INSERT INTO project_categories (org_id, name, description, color, created_at, updated_at) VALUES (?, ?, ?, ?, NOW(), NOW())",
                [Auth::orgId(), $name, sanitize($this->getPost('description', '')), $this->getPost('color', '#0A3A6B')]
            );
            $this->setFlash('success', 'Category created.');
        }

        $this->redirect(APP_URL . '/projects/categories');
    }

    public function editCategory(int $id): void
    {
        $this->requireAuth();
        if (!$this->validateCsrf()) {
            $this->redirect(APP_URL . '/projects/categories');
        }

        $name = sanitize($this->getPost('name', ''));
        if (!empty($name)) {
            $this->dbExecute(
                "UPDATE project_categories SET name = ?, description = ?, color = ?, updated_at = NOW() WHERE id = ? AND org_id = ?",
                [$name, sanitize($this->getPost('description', '')), $this->getPost('color', '#0A3A6B'), $id, Auth::orgId()]
            );
            $this->setFlash('success', 'Category updated.');
        }

        $this->redirect(APP_URL . '/projects/categories');
    }

    public function deleteCategory(int $id): void
    {
        $this->requireAuth();
        $this->dbExecute("DELETE FROM project_categories WHERE id = ? AND org_id = ?", [$id, Auth::orgId()]);
        $this->setFlash('success', 'Category deleted.');
        $this->redirect(APP_URL . '/projects/categories');
    }

    public function assignContractors(int $projectId): void
    {
        $this->requireAuth();
        if (!$this->validateCsrf()) {
            $this->redirect(APP_URL . "/projects/{$projectId}");
        }

        $project = $this->projectModel->find($projectId);
        if (!$project || (int)$project['org_id'] !== (int)Auth::orgId()) {
            $this->setFlash('error', 'Project not found.');
            $this->redirect(APP_URL . '/projects');
        }

        $contractorIds = $_POST['contractors'] ?? [];
        foreach ($contractorIds as $cId) {
            $cId = (int) $cId;
            if (!$cId) {
                continue;
            }
            $this->projectModel->assignContractor($projectId, $cId, Auth::id());
            $this->notifyContractorAssignment($projectId, $cId);
        }

        $this->setFlash('success', 'Contractors assigned.');
        $this->redirect(APP_URL . "/projects/{$projectId}");
    }

    public function contractorResponse(int $projectId): void
    {
        $this->requireAuth();
        $user = Auth::user();
        $contractorId = (int)($user['entity_id'] ?? 0);

        if ($_SERVER['REQUEST_METHOD'] === 'POST') {
            if (!$this->validateCsrf()) {
                $this->redirect(APP_URL . "/projects/{$projectId}");
            }

            $status = $this->getPost('response_status', 'accepted');
            $comment = sanitize($this->getPost('comment', ''));

            if ($status === 'rejected' && empty($comment)) {
                $this->setFlash('error', 'Comment required for rejection.');
                $this->redirect(APP_URL . "/projects/{$projectId}/contractor-response");
            }

            $this->projectModel->updateContractorStatus($projectId, $contractorId, $status, $comment);
            logAudit('contractor_response', 'projects', 'project_contractor', $projectId, null, ['contractor_id' => $contractorId, 'status' => $status]);
            $this->setFlash('success', 'Your response has been submitted.');
            $this->redirect(APP_URL . "/dashboard");
            return;
        }

        $project = $this->projectModel->getWithDetails($projectId);
        if (!$project) {
            $this->setFlash('error', 'Project not found.');
            $this->redirect(APP_URL . '/dashboard');
        }
        $this->render('projects/contractor_response', [
            'pageTitle' => 'Contractor Response',
            'project' => $project
        ]);
    }

    public function uploadDocument(int $projectId): void
    {
        $this->requireAuth();
        $project = $this->projectModel->find($projectId);
        if (!$project || (int)$project['org_id'] !== (int)Auth::orgId()) {
            $this->setFlash('error', 'Project not found.');
            $this->redirect(APP_URL . '/projects');
        }

        if (!empty($_FILES['document']['name'])) {
            $docType = $this->getPost('document_type', 'supporting');
            $upload = $this->uploadFile($_FILES['document'], 'projects/supporting');
            if ($upload) {
                $this->saveProjectDocument($projectId, $docType, $upload, $_FILES['document']['type']);
                $this->setFlash('success', 'Document uploaded.');
            }
        }
        $this->redirect(APP_URL . "/projects/{$projectId}");
    }

    public function deleteDocument(int $docId): void
    {
        $this->requireAuth();
        $doc = $this->dbQueryOne("SELECT * FROM project_documents WHERE id = ?", [$docId]);
        if ($doc) {
            $project = $this->projectModel->find((int)$doc['project_id']);
            if ($project && (int)$project['org_id'] === (int)Auth::orgId()) {
                $this->dbExecute("DELETE FROM project_documents WHERE id = ?", [$docId]);
                $this->setFlash('success', 'Document deleted.');
            }
            $this->redirect(APP_URL . "/projects/{$doc['project_id']}");
        }
        $this->redirect(APP_URL . "/projects");
    }

    public function milestones(int $projectId): void
    {
        $this->requireAuth();
        $milestones = $this->projectModel->getMilestones($projectId);
        $this->json($milestones);
    }

    public function createMilestone(int $projectId): void
    {
        $this->requireAuth();
        if ($this->validateCsrf()) {
            $data = [
                'project_id' => $projectId,
                'title' => sanitize($this->getPost('title', '')),
                'description' => sanitize($this->getPost('description', '')),
                'due_date' => $this->getPost('due_date'),
                'created_by' => Auth::id()
            ];
            $this->projectModel->addMilestone($data);
            $this->setFlash('success', 'Milestone added.');
        }
        $this->redirect(APP_URL . "/projects/{$projectId}");
    }

    public function updateMilestone(int $milestoneId): void
    {
        $this->requireAuth();
        if (!$this->validateCsrf()) {
            $this->jsonError('Invalid security token.');
        }
        $status = $this->getPost('status', 'completed');
        $completedDate = $status === 'completed' ? date('Y-m-d') : null;
        $this->dbExecute(
            "UPDATE project_milestones SET status = ?, completed_date = ?, updated_at = NOW() WHERE id = ?",
            [$status, $completedDate, $milestoneId]
        );
        $this->json(['success' => true]);
    }

    public function deleteMilestone(int $milestoneId): void
    {
        $this->requireAuth();
        if (!$this->validateCsrf()) {
            $this->jsonError('Invalid security token.');
        }
        $this->dbExecute("DELETE FROM project_milestones WHERE id = ?", [$milestoneId]);
        $this->json(['success' => true]);
    }

    private function saveProjectDocument(int $projectId, string $docType, array $upload, string $mimeType): void
    {
        $this->dbExecute(
            "INSERT INTO project_documents (project_id, document_type, document_name, file_path, file_size, mime_type, uploaded_by, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, NOW())",
            [$projectId, $docType, $upload['name'], $upload['path'], $upload['size'], $mimeType, Auth::id()]
        );
    }

    private function notifyContractorAssignment(int $projectId, int $contractorId): void
    {
        $project = $this->dbQueryOne(
            "SELECT p.*, c.company_name as client_name FROM projects p LEFT JOIN clients c ON p.client_id = c.id WHERE p.id = ?",
            [$projectId]
        );
        if (!$project) {
            return;
        }

        $contractorUsers = $this->dbQuery(
            "SELECT u.* FROM contractor_users cu INNER JOIN users u ON cu.user_id = u.id WHERE cu.contractor_id = ? AND u.is_active = 1",
            [$contractorId]
        );

        $actionUrl = APP_URL . '/projects/' . $projectId . '/contractor-response';
        $assignedBy = Auth::user();
        $assignedByName = trim(($assignedBy['first_name'] ?? '') . ' ' . ($assignedBy['last_name'] ?? ''));

        foreach ($contractorUsers as $user) {
            createNotification(
                (int)$user['id'],
                'project_assigned',
                'New Project Assignment',
                'You have been assigned to project ' . $project['name'] . '. Please accept or reject.',
                ['project_id' => $projectId]
            );

            $htmlBody = $this->renderProjectAssignedEmail([
                'recipientName' => trim($user['first_name'] . ' ' . $user['last_name']),
                'projectName' => $project['name'],
                'projectCode' => $project['project_code'],
                'clientName' => $project['client_name'] ?? 'N/A',
                'projectValue' => formatCurrency((float)$project['project_value'], $project['currency'] ?? DEFAULT_CURRENCY),
                'startDate' => $project['start_date'] ? formatDate($project['start_date']) : 'TBD',
                'endDate' => $project['expected_end_date'] ? formatDate($project['expected_end_date']) : 'TBD',
                'location' => $project['location'] ?? '',
                'actionUrl' => $actionUrl,
                'assignedBy' => $assignedByName,
            ]);
            sendEmail($user['email'], trim($user['first_name'] . ' ' . $user['last_name']), 'New Project Assignment — ' . $project['name'], $htmlBody);
        }

        $this->projectModel->markContractorNotified($projectId, $contractorId);
    }

    private function notifyProjectManager(int $projectId, int $userId): void
    {
        $project = $this->dbQueryOne("SELECT name, project_code FROM projects WHERE id = ?", [$projectId]);
        $user = $this->dbQueryOne("SELECT * FROM users WHERE id = ?", [$userId]);
        if (!$project || !$user) {
            return;
        }

        createNotification(
            $userId,
            'project_manager_assigned',
            'Assigned as Project Manager',
            'You have been assigned as project manager for ' . $project['name'] . '.',
            ['project_id' => $projectId]
        );

        $url = APP_URL . '/projects/' . $projectId;
        $htmlBody = "<p>Hello " . htmlspecialchars($user['first_name']) . ",</p>" .
            "<p>You have been assigned as the project manager for <strong>" . htmlspecialchars($project['name']) . "</strong> (" . htmlspecialchars($project['project_code']) . ").</p>" .
            "<p><a href=\"{$url}\">View Project</a></p>";
        sendEmail($user['email'], trim($user['first_name'] . ' ' . $user['last_name']), 'Assigned as Project Manager — ' . $project['name'], $htmlBody);
    }

    private function renderProjectAssignedEmail(array $data): string
    {
        extract($data);
        ob_start();
        $templatePath = ROOT_PATH . '/modules/auth/views/email_templates/project_assigned.php';
        if (file_exists($templatePath)) {
            include $templatePath;
        }
        return ob_get_clean();
    }
}
