<?php

namespace App\Services;

use App\Models\AuditLog;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;

class AuditService
{
    /**
     * Write an audit log entry with hash chaining.
     *
     * @param  string       $action      AuditLog::ACTION_* constant
     * @param  string       $actorType   AuditLog::ACTOR_* constant
     * @param  int|null     $actorId     Authenticated user's ID (null for public/system)
     * @param  string|null  $actorEmail  Authenticated user's email
     * @param  string       $status      AuditLog::STATUS_SUCCESS or STATUS_FAILED
     * @param  string|null  $recordType  Model class shortname, e.g. 'CompanyRegistration'
     * @param  int|null     $recordId    Primary key of the affected record
     * @param  array        $metadata    Action-specific extra data (never include NIN/BVN values)
     * @param  string|null  $ipAddress   Request IP address
     */
    public function log(
        string $action,
        string $actorType,
        ?int $actorId,
        ?string $actorEmail,
        string $status,
        ?string $recordType,
        ?int $recordId,
        array $metadata = [],
        ?string $ipAddress = null,
    ): AuditLog {
        $now = now();

        // Get hash of the most recent entry for chaining
        $prevHash = DB::table('audit_logs')->orderByDesc('id')->value('current_hash');

        // Build the canonical string for this entry's hash
        $hashInput = implode('|', [
            $now->toIso8601String(),
            $action,
            $actorType,
            (string) $actorId,
            (string) $actorEmail,
            (string) $ipAddress,
            (string) $recordType,
            (string) $recordId,
            $status,
            json_encode($metadata),
            (string) $prevHash,
        ]);

        $currentHash = hash('sha256', $hashInput);

        return AuditLog::create([
            'timestamp'    => $now,
            'action'       => $action,
            'actor_type'   => $actorType,
            'actor_id'     => $actorId,
            'actor_email'  => $actorEmail,
            'ip_address'   => $ipAddress,
            'record_type'  => $recordType,
            'record_id'    => $recordId,
            'status'       => $status,
            'metadata'     => empty($metadata) ? null : $metadata,
            'prev_hash'    => $prevHash,
            'current_hash' => $currentHash,
            'created_at'   => $now,
        ]);
    }

    /**
     * Convenience: log a SUCCESS entry.
     */
    public function success(
        string $action,
        string $actorType,
        ?int $actorId,
        ?string $actorEmail,
        ?string $recordType = null,
        ?int $recordId = null,
        array $metadata = [],
        ?string $ipAddress = null,
    ): AuditLog {
        return $this->log($action, $actorType, $actorId, $actorEmail, AuditLog::STATUS_SUCCESS, $recordType, $recordId, $metadata, $ipAddress);
    }

    /**
     * Convenience: log a FAILED entry.
     */
    public function failed(
        string $action,
        string $actorType,
        ?int $actorId,
        ?string $actorEmail,
        ?string $recordType = null,
        ?int $recordId = null,
        array $metadata = [],
        ?string $ipAddress = null,
    ): AuditLog {
        return $this->log($action, $actorType, $actorId, $actorEmail, AuditLog::STATUS_FAILED, $recordType, $recordId, $metadata, $ipAddress);
    }

    /**
     * Query audit logs with optional filters, paginated.
     *
     * Supported filters (all optional):
     *   action       — exact action constant
     *   actor_type   — admin | portal_user | system | public
     *   actor_email  — exact email match
     *   record_type  — e.g. CompanyRegistration
     *   record_id    — integer
     *   status       — SUCCESS | FAILED
     *   date_from    — Y-m-d (inclusive, based on timestamp column)
     *   date_to      — Y-m-d (inclusive)
     *
     * @param  array  $filters
     * @param  int    $perPage  10–100
     * @return LengthAwarePaginator
     */
    public function list(array $filters = [], int $perPage = 50): LengthAwarePaginator
    {
        $query = AuditLog::query()->orderByDesc('id');

        if (!empty($filters['action'])) {
            $query->where('action', $filters['action']);
        }

        if (!empty($filters['actor_type'])) {
            $query->where('actor_type', $filters['actor_type']);
        }

        if (!empty($filters['actor_email'])) {
            $query->where('actor_email', $filters['actor_email']);
        }

        if (!empty($filters['record_type'])) {
            $query->where('record_type', $filters['record_type']);
        }

        if (!empty($filters['record_id'])) {
            $query->where('record_id', (int) $filters['record_id']);
        }

        if (!empty($filters['status'])) {
            $query->where('status', strtoupper($filters['status']));
        }

        if (!empty($filters['date_from'])) {
            $query->whereDate('timestamp', '>=', $filters['date_from']);
        }

        if (!empty($filters['date_to'])) {
            $query->whereDate('timestamp', '<=', $filters['date_to']);
        }

        return $query->paginate($perPage);
    }

    /**
     * Build actor context from the current request's authenticated user.
     * Returns [actorType, actorId, actorEmail, ip].
     *
     * Pass the Request object from the controller; this method resolves
     * which guard is active and extracts actor details.
     */
    public function actorFromRequest(Request $request): array
    {
        $ip = $request->ip();

        // Admin guard (sanctum)
        if ($admin = $request->user('sanctum')) {
            return [AuditLog::ACTOR_ADMIN, $admin->id, $admin->email, $ip];
        }

        // Portal guard
        if ($portalUser = $request->user('portal_api')) {
            return [AuditLog::ACTOR_PORTAL_USER, $portalUser->id, $portalUser->email, $ip];
        }

        // Unauthenticated (public form submissions)
        return [AuditLog::ACTOR_PUBLIC, null, null, $ip];
    }
}
