<?php
declare(strict_types=1);

namespace App\Models;

use Core\DB;
use Core\Model;

final class Attachment extends Model
{
    protected string $table = 'attachments';
    protected string $primaryKey = 'attachment_id';

    public const OWNERS = ['asset', 'incident', 'work_order', 'expense', 'reading_session', 'round_run_stop', 'sds', 'training_record'];

    public static function forOwner(int $tenantId, string $ownerType, int $ownerId): array
    {
        return DB::all(
            'SELECT * FROM attachments
              WHERE tenant_id = :t AND owner_type = :ot AND owner_id = :oid AND deleted_at IS NULL
              ORDER BY created_at DESC',
            ['t' => $tenantId, 'ot' => $ownerType, 'oid' => $ownerId]
        );
    }

    public static function create(int $tenantId, int $userId, string $ownerType, int $ownerId, array $file, ?string $caption = null): int
    {
        return (int) DB::insert('attachments', [
            'tenant_id'     => $tenantId,
            'owner_type'    => $ownerType,
            'owner_id'      => $ownerId,
            'file_name'     => $file['file_name'],
            'original_name' => $file['original_name'],
            'file_ext'      => $file['file_ext'] ?? null,
            'mime_type'     => $file['mime_type'] ?? null,
            'size_bytes'    => $file['size_bytes'] ?? null,
            'caption'       => $caption,
            'uploaded_by'   => $userId,
        ]);
    }

    public static function findInTenant(int $tenantId, int $attachmentId): ?array
    {
        return DB::one(
            'SELECT * FROM attachments WHERE tenant_id = :t AND attachment_id = :id AND deleted_at IS NULL',
            ['t' => $tenantId, 'id' => $attachmentId]
        );
    }

    public static function softDelete(int $tenantId, int $attachmentId): void
    {
        DB::run(
            'UPDATE attachments SET deleted_at = NOW() WHERE tenant_id = :t AND attachment_id = :id',
            ['t' => $tenantId, 'id' => $attachmentId]
        );
    }

    /**
     * Save an uploaded PHP file to storage/uploads/{owner_type}/YYYY/MM/ and return
     * the array shape the create() method expects.
     */
    public static function moveUploaded(string $ownerType, array $phpFile): array
    {
        if (!isset($phpFile['tmp_name'], $phpFile['name']) || $phpFile['error'] !== UPLOAD_ERR_OK) {
            throw new \RuntimeException('Upload failed with error code ' . ($phpFile['error'] ?? '?'));
        }
        $max = (int) env('UPLOAD_MAX_BYTES', 10485760);
        if (($phpFile['size'] ?? 0) > $max) {
            throw new \RuntimeException('File too large — max is ' . $max . ' bytes.');
        }

        $orig = (string) $phpFile['name'];
        $ext  = strtolower(pathinfo($orig, PATHINFO_EXTENSION));
        $safeExt = preg_match('/^[a-z0-9]{1,10}$/', $ext) ? $ext : 'bin';

        $y = date('Y'); $m = date('m');
        $dir = STORAGE . DS . 'uploads' . DS . $ownerType . DS . $y . DS . $m;
        if (!is_dir($dir) && !@mkdir($dir, 0775, true) && !is_dir($dir)) {
            throw new \RuntimeException('Failed to create upload directory: ' . $dir);
        }

        $stem = date('YmdHis') . '_' . bin2hex(random_bytes(4));
        $filename = $stem . '.' . $safeExt;
        $target = $dir . DS . $filename;
        if (!move_uploaded_file($phpFile['tmp_name'], $target)) {
            throw new \RuntimeException('Failed to move uploaded file.');
        }

        return [
            'file_name'     => $ownerType . '/' . $y . '/' . $m . '/' . $filename, // path relative to storage/uploads/
            'original_name' => $orig,
            'file_ext'      => $safeExt,
            'mime_type'     => $phpFile['type'] ?? mime_content_type($target) ?: null,
            'size_bytes'    => (int) filesize($target),
        ];
    }

    /** Absolute filesystem path for the stored file. */
    public static function absolutePath(string $fileName): string
    {
        return STORAGE . DS . 'uploads' . DS . str_replace('/', DS, $fileName);
    }
}
