<?php

namespace App\Models;

use App\Core\Database;

class ActivityLog
{
    public static function log(array $actor, string $action, string $description, ?int $departmentId = null, ?int $projectId = null): void
    {
        $db = Database::connection();
        $db->prepare(
            "INSERT INTO activity_log (user_type, user_id, user_name, department_id, project_id, action, description, ip)
             VALUES (:t, :id, :n, :d, :p, :a, :desc, :ip)"
        )->execute([
            't' => $actor['is_super_admin'] ?? false ? 'superadmin' : $actor['user_type'],
            'id' => $actor['user_id'],
            'n' => $actor['name'],
            'd' => $departmentId,
            'p' => $projectId,
            'a' => $action,
            'desc' => $description,
            'ip' => $_SERVER['REMOTE_ADDR'] ?? '',
        ]);
    }

    /**
     * Activity visible to this user, per standard role-based access:
     * - a manager/employee sees their own activity, plus that of anyone who
     *   reports directly to them (their team)
     * - a super admin sees everything
     */
    // Employee Onboarding's project id, looked up once. Used only to give
    // that one project's activity rows strict self-only visibility below —
    // every other project's rows are completely unaffected by this lookup
    // or by the branch that uses it.
    private static ?int $onbProjectIdCache = null;
    private static function onboardingProjectId(): ?int
    {
        if (self::$onbProjectIdCache !== null) return self::$onbProjectIdCache;
        $db = Database::connection();
        $row = $db->query("SELECT id FROM projects WHERE code = 'employee_onboarding' LIMIT 1")->fetch();
        self::$onbProjectIdCache = $row ? (int)$row['id'] : 0; // 0 = "no such project", clause below then never matches
        return self::$onbProjectIdCache;
    }

    public static function visibleTo(array $user, int $limit = 15): array
    {
        $db = Database::connection();

        if (!empty($user['is_super_admin'])) {
            $stmt = $db->prepare("SELECT * FROM activity_log ORDER BY created_at DESC LIMIT :lim");
            $stmt->bindValue('lim', $limit, \PDO::PARAM_INT);
            $stmt->execute();
            return $stmt->fetchAll();
        }

        // Self + everyone below this manager in the reporting chain, however
        // many levels deep (direct reports, their reports, and so on). This
        // matters for e.g. sara.lasher: Karan/Harpreet report to Cesar, not
        // to her directly, but she still needs full visibility into their
        // activity.
        $managerIds = [(int)$user['user_id']];
        $employeeIds = [];
        if ($user['user_type'] === 'manager') {
            $sub = \App\Models\UserManager::allSubordinateIds((int)$user['user_id']);
            $managerIds = array_values(array_unique(array_merge($managerIds, $sub['manager'])));
            $employeeIds = $sub['employee'];
        }

        $clauses = [];
        $params = [];

        $placeholders = implode(',', array_fill(0, count($managerIds), '?'));
        $clauses[] = "(user_type = ? AND user_id IN ($placeholders))";
        $params[] = $user['user_type'];
        foreach ($managerIds as $mid) $params[] = $mid;

        if (!empty($employeeIds)) {
            $placeholders2 = implode(',', array_fill(0, count($employeeIds), '?'));
            $clauses[] = "(user_type = 'employee' AND user_id IN ($placeholders2))";
            foreach ($employeeIds as $eid) $params[] = $eid;
        }

        // For submitters (Karan/Harpreet), also surface activity that
        // managers performed ON their KPI entries — e.g. "Cesar approved
        // your KPI" appears in Karan's Recent Activity even though it was
        // logged under Cesar's user_id. We do this by matching descriptions
        // that mention this user's name, but a cleaner approach is to also
        // show manager actions tagged with KPI actions on scheduling entries
        // submitted by this user.  We achieve that by querying the activity
        // log for any kpi_approved / kpi_returned / kpi_score_revised /
        // kpi_comment_added actions whose description contains the user's name.
        if ($user['user_type'] === 'manager' && !empty($user['name'])) {
            // This catches Cesar's review of Karan/Harpreet showing up for them.
            $nameEscaped = str_replace(['%','_'], ['\\%','\\_'], $user['name']);
            $clauses[] = "(action IN ('kpi_approved','kpi_returned','kpi_score_revised','kpi_comment_added') AND description LIKE ?)";
            $params[] = '%' . $nameEscaped . '%';
        }

        // Employee Onboarding: strict self-only visibility, regardless of
        // where this user sits in the manager/subordinate hierarchy. This
        // is a deliberate exception scoped by project_id — it does not
        // change how any other project's rows are matched above; it only
        // wraps those existing hierarchy clauses so they explicitly EXCLUDE
        // onboarding-project rows, and adds one narrow OR-branch that
        // includes onboarding rows only when they belong to this exact
        // authenticated user.
        $onbProjectId = self::onboardingProjectId();
        $hierarchySql = '(' . implode(' OR ', $clauses) . ')';
        $stmt = $db->prepare(
            "SELECT * FROM activity_log WHERE
               ($hierarchySql AND (project_id IS NULL OR project_id != ?))
               OR (project_id = ? AND user_type = ? AND user_id = ?)
             ORDER BY created_at DESC LIMIT " . (int)$limit
        );
        $params[] = $onbProjectId;
        $params[] = $onbProjectId;
        $params[] = $user['user_type'];
        $params[] = (int)$user['user_id'];
        $stmt->execute($params);
        return $stmt->fetchAll();
    }
}
