<?php
/**
 * Attachment Record model for StaticQ
 * 
 * Represents a single registered attachment with its tracking data.
 * 
 * IMPORTANT: WordPress postmeta (_wp_attachment_metadata) is the source of truth
 * for image sizes. This record only tracks:
 * - Where files should be stored (storage_location)
 * - Whether WebP generation is complete (has_webp)
 * - Path information for file operations
 * - Processing status
 * 
 * @package suspended-cloud/cloudflare-media-offload
 * @since 3.0.0
 */

if (!defined('ABSPATH')) {
    exit;
}

class SQMedia_Attachment_Record {
    
    /**
     * Database row data
     */
    private $data;
    
    /**
     * WordPress attachment metadata (cached)
     */
    private $wp_metadata;
    
    /**
     * Upload directory info
     */
    private $upload_dir;
    
    /**
     * Constructor
     */
    public function __construct(?object $data = null) {
        $this->data = $data;
        $this->upload_dir = wp_upload_dir();
    }
    
    /**
     * Load record by attachment ID
     */
    public static function find(int $attachment_id): ?self {
        $db = sqmedia()->get_database();
        $data = $db->get_record($attachment_id);
        
        if (!$data) {
            return null;
        }
        
        return new self($data);
    }
    
    /**
     * Create new record from attachment
     */
    public static function create_from_attachment(int $attachment_id, array $options = []): ?self {
        $db = sqmedia()->get_database();
        
        // Check not already registered
        if ($db->is_registered($attachment_id)) {
            return self::find($attachment_id);
        }
        
        // Get WordPress metadata
        $metadata = wp_get_attachment_metadata($attachment_id);
        
        // Get file path directly from postmeta to avoid WP Offload Media hijacking get_attached_file()
        $file = get_post_meta($attachment_id, '_wp_attached_file', true);
        
        if (!$metadata || !$file) {
            return null;
        }
        
        // If the file path looks like a cloud URL (from WP Offload Media), skip it
        if (preg_match('#^[a-z0-9]+://#i', $file)) {
            return null;
        }
        
        // Parse file path
        $relative_path = $file;
        $master_dir = dirname($relative_path);
        $master_filename = basename($relative_path);
        
        if ($master_dir === '.') {
            $master_dir = '';
        }
        
        // Build record data (NEW simplified schema)
        $data = [
            'attachment_id' => $attachment_id,
            'storage_location' => $options['storage_location'] ?? 'local',
            'master_path' => $relative_path,
            'master_dir' => $master_dir,
            'master_filename' => $master_filename,
            'original_image' => $metadata['original_image'] ?? null,
            // Same rule as the registration handler: don't claim a location
            // we haven't verified. The cron handler writes a concrete value
            // after processing; the Media Library Scanner aligns NULLs from
            // legacy/migration records to observed state.
            'original_location' => null,
            'has_webp' => $options['has_webp'] ?? 0,
            'registered_via' => $options['registered_via'] ?? 'upload',
            'batch_id' => $options['batch_id'] ?? null,
            'status' => $options['status'] ?? 'pending',
        ];
        
        $id = $db->create_record($data);
        
        if (!$id) {
            return null;
        }
        
        return self::find($attachment_id);
    }
    
    /**
     * Check if record is loaded
     */
    public function is_loaded(): bool {
        return $this->data !== null;
    }
    
    /**
     * Get record ID
     */
    public function get_id(): ?int {
        return $this->data->id ?? null;
    }
    
    /**
     * Get attachment ID
     */
    public function get_attachment_id(): ?int {
        return $this->data->attachment_id ?? null;
    }
    
    // =========================================================================
    // MEDIA TYPE DETECTION
    // =========================================================================
    
    /**
     * Check if this attachment is an image
     * 
     * @return bool True if image, false for other media types
     */
    public function is_image(): bool {
        $attachment_id = $this->get_attachment_id();
        if (!$attachment_id) {
            return false;
        }
        
        $mime_type = get_post_mime_type($attachment_id);
        return $mime_type && strpos($mime_type, 'image/') === 0;
    }
    
    /**
     * Get the MIME type of this attachment
     * 
     * @return string|null MIME type or null
     */
    public function get_mime_type(): ?string {
        $attachment_id = $this->get_attachment_id();
        if (!$attachment_id) {
            return null;
        }
        
        $mime = get_post_mime_type($attachment_id);
        return $mime ?: null;
    }
    
    /**
     * Get the general media type (image, video, audio, document, other)
     * 
     * @return string Media type category
     */
    public function get_media_type(): string {
        $mime = $this->get_mime_type();
        
        if (!$mime) {
            return 'other';
        }
        
        if (strpos($mime, 'image/') === 0) {
            return 'image';
        }
        if (strpos($mime, 'video/') === 0) {
            return 'video';
        }
        if (strpos($mime, 'audio/') === 0) {
            return 'audio';
        }
        if (strpos($mime, 'application/pdf') === 0 || 
            strpos($mime, 'application/msword') === 0 ||
            strpos($mime, 'application/vnd.') === 0 ||
            strpos($mime, 'text/') === 0) {
            return 'document';
        }
        
        return 'other';
    }
    
    /**
     * Check if this attachment supports bitmap resizing
     * 
     * SVG and other vector formats don't need resizing - they scale infinitely.
     * Only raster/bitmap images should have thumbnails generated.
     * 
     * @return bool True if resizing is supported
     */
    public function supports_resizing(): bool {
        if (!$this->is_image()) {
            return false;
        }
        
        $mime = $this->get_mime_type();
        
        // Only bitmap/raster images support resizing
        // SVG is vector, ICO is special format - neither needs thumbnails
        return in_array($mime, [
            'image/jpeg',
            'image/jpg',
            'image/png',
            'image/gif',
            'image/webp',
            'image/bmp',
        ], true);
    }
    
    /**
     * Check if this attachment requires size generation (resizable images only)
     * 
     * @return bool True if size generation applies
     */
    public function requires_size_generation(): bool {
        return $this->supports_resizing();
    }
    
    /**
     * Check if this attachment can have WebP variants (supported images only)
     * 
     * @return bool True if WebP can be generated
     */
    public function supports_webp(): bool {
        if (!$this->is_image()) {
            return false;
        }
        
        $mime = $this->get_mime_type();
        
        // WebP can be generated from JPEG, PNG, GIF
        return in_array($mime, [
            'image/jpeg',
            'image/jpg',
            'image/png',
            'image/gif',
        ], true);
    }
    
    // =========================================================================
    // STORAGE & WEBP FLAGS
    // =========================================================================
    
    /**
     * Get storage location (where files should be stored)
     */
    public function get_storage_location(): string {
        return $this->data->storage_location ?? 'local';
    }
    
    /**
     * Set storage location
     */
    public function set_storage_location(string $location): self {
        $this->data->storage_location = $location;
        return $this;
    }
    
    /**
     * Check if WebP sizes exist
     * Check if WebP versions exist
     */
    public function has_webp(): bool {
        return (bool) ($this->data->has_webp ?? 0);
    }
    
    /**
     * Set WebP flag
     */
    public function set_has_webp(bool $value): self {
        $this->data->has_webp = $value ? 1 : 0;
        return $this;
    }
    
    // =========================================================================
    // MASTER FILE
    // =========================================================================
    
    /**
     * Get master file path (relative to uploads)
     */
    public function get_master_path(): string {
        return $this->data->master_path ?? '';
    }
    
    /**
     * Get master directory (relative to uploads)
     */
    public function get_master_dir(): string {
        return $this->data->master_dir ?? '';
    }
    
    /**
     * Get master filename
     */
    public function get_master_filename(): string {
        return $this->data->master_filename ?? '';
    }
    
    /**
     * Get master local path (absolute)
     */
    public function get_master_local_path(): string {
        return $this->upload_dir['basedir'] . '/' . $this->get_master_path();
    }
    
    /**
     * Get master bucket path
     */
    public function get_master_bucket_path(): string {
        return SQMedia_Settings::get_current_prefix() . '/' . $this->get_master_path();
    }
    
    // =========================================================================
    // ORIGINAL IMAGE (if scaled)
    // =========================================================================
    
    /**
     * Get original image filename (if image was scaled on upload)
     */
    public function get_original_image(): ?string {
        // First check plugin database
        if (!empty($this->data->original_image)) {
            return $this->data->original_image;
        }
        
        // Fallback to WordPress metadata
        $metadata = $this->get_wp_metadata();
        if (!empty($metadata['original_image'])) {
            return $metadata['original_image'];
        }
        
        return null;
    }
    
    /**
     * Get the per-attachment original location ('alongside', 'originals', or null)
     */
    public function get_original_location(): ?string {
        return $this->data->original_location ?? null;
    }

    /**
     * Check if has original image
     */
    public function has_original_image(): bool {
        return !empty($this->get_original_image());
    }
    
    /**
     * Get original image local path. Honors `original_location`: returns the
     * `originals/<master_dir>/` path when the record was placed there
     * (`move_to_originals` mode), otherwise returns the alongside-master path.
     */
    public function get_original_local_path(): ?string {
        $original = $this->get_original_image();
        if (!$original) {
            return null;
        }

        $dir = $this->get_master_dir();
        $rel = ($dir ? $dir . '/' : '') . $original;
        if ($this->get_original_location() === 'originals') {
            $rel = 'originals/' . $rel;
        }
        return $this->upload_dir['basedir'] . '/' . $rel;
    }

    /**
     * Get original image bucket path. Honors `original_location` the same
     * way as get_original_local_path().
     */
    public function get_original_bucket_path(): ?string {
        $original = $this->get_original_image();
        if (!$original) {
            return null;
        }

        $dir = $this->get_master_dir();
        $rel = ($dir ? $dir . '/' : '') . $original;
        if ($this->get_original_location() === 'originals') {
            $rel = 'originals/' . $rel;
        }
        return SQMedia_Settings::get_current_prefix() . '/' . $rel;
    }
    
    // =========================================================================
    // STATUS & TRACKING
    // =========================================================================
    
    /**
     * Get status
     */
    public function get_status(): string {
        return $this->data->status ?? 'pending';
    }
    
    /**
     * Set status
     */
    public function set_status(string $status): self {
        $this->data->status = $status;
        return $this;
    }
    
    /**
     * Get batch ID
     */
    public function get_batch_id(): ?string {
        return $this->data->batch_id ?? null;
    }
    
    /**
     * Get registered via
     */
    public function get_registered_via(): string {
        return $this->data->registered_via ?? 'upload';
    }
    
    /**
     * Get registered at timestamp
     */
    public function get_registered_at(): ?string {
        return $this->data->registered_at ?? null;
    }
    
    /**
     * Get last verified timestamp
     */
    public function get_last_verified(): ?string {
        return $this->data->last_verified ?? null;
    }
    
    // =========================================================================
    // WORDPRESS METADATA (Source of truth for sizes)
    // =========================================================================
    
    /**
     * Get WordPress attachment metadata
     * This is the source of truth for image sizes
     */
    public function get_wp_metadata(): array {
        if ($this->wp_metadata === null) {
            $this->wp_metadata = wp_get_attachment_metadata($this->get_attachment_id()) ?: [];
        }
        return $this->wp_metadata;
    }
    
    /**
     * Clear cached WordPress metadata (force reload)
     */
    public function clear_wp_metadata_cache(): self {
        $this->wp_metadata = null;
        return $this;
    }
    
    /**
     * Get sizes from WordPress metadata
     */
    public function get_wp_sizes(): array {
        $metadata = $this->get_wp_metadata();
        return $metadata['sizes'] ?? [];
    }
    
    /**
     * Get a specific size from WordPress metadata
     */
    public function get_wp_size(string $size_name): ?array {
        $sizes = $this->get_wp_sizes();
        return $sizes[$size_name] ?? null;
    }
    
    /**
     * Get only non-WebP sizes from WordPress metadata
     */
    public function get_wp_image_sizes(): array {
        return array_filter(
            $this->get_wp_sizes(),
            fn($key) => !str_ends_with($key, '-webp'),
            ARRAY_FILTER_USE_KEY
        );
    }
    
    /**
     * Get only WebP sizes from WordPress metadata
     */
    public function get_wp_webp_sizes(): array {
        return array_filter(
            $this->get_wp_sizes(),
            fn($key) => str_ends_with($key, '-webp'),
            ARRAY_FILTER_USE_KEY
        );
    }
    
    // =========================================================================
    // URL HELPERS
    // =========================================================================
    
    /**
     * Get WordPress attachment URL
     */
    public function get_wp_url(): string {
        return wp_get_attachment_url($this->get_attachment_id()) ?: '';
    }
    
    /**
     * Get Cloudflare URL for master
     */
    public function get_cloudflare_url(): string {
        return SQMedia_Settings::build_bucket_url($this->get_master_path());
    }
    
    /**
     * Get appropriate URL based on storage location
     */
    public function get_url(): string {
        if ($this->get_storage_location() === 'local') {
            return $this->get_wp_url();
        }
        return $this->get_cloudflare_url();
    }
    
    /**
     * Get URL for a specific size
     */
    public function get_size_url(string $size_name): string {
        $size = $this->get_wp_size($size_name);
        
        if (!$size) {
            return '';
        }
        
        $dir = $this->get_master_dir();
        $relative_path = ($dir ? $dir . '/' : '') . $size['file'];
        
        if ($this->get_storage_location() === 'local') {
            return $this->upload_dir['baseurl'] . '/' . $relative_path;
        }
        
        return SQMedia_Settings::build_bucket_url($relative_path);
    }
    
    // =========================================================================
    // FILE PATH HELPERS
    // =========================================================================
    
    /**
     * Get local path for a size
     */
    public function get_size_local_path(string $size_name): ?string {
        $size = $this->get_wp_size($size_name);
        if (!$size || empty($size['file'])) {
            return null;
        }
        
        $dir = $this->get_master_dir();
        return $this->upload_dir['basedir'] . '/' . ($dir ? $dir . '/' : '') . $size['file'];
    }
    
    /**
     * Get bucket path for a size
     */
    public function get_size_bucket_path(string $size_name): ?string {
        $size = $this->get_wp_size($size_name);
        if (!$size || empty($size['file'])) {
            return null;
        }
        
        $dir = $this->get_master_dir();
        return SQMedia_Settings::get_current_prefix() . '/' . ($dir ? $dir . '/' : '') . $size['file'];
    }
    
    /**
     * Get WebP filename for a given source filename
     */
    public static function get_webp_filename(string $filename): string {
        return preg_replace('/\.(jpe?g|png|gif)$/i', '.webp', $filename);
    }
    
    /**
     * Build file path from filename
     */
    public function build_path(string $filename): string {
        $dir = $this->get_master_dir();
        return ($dir ? $dir . '/' : '') . $filename;
    }
    
    /**
     * Build local path from filename
     */
    public function build_local_path(string $filename): string {
        return $this->upload_dir['basedir'] . '/' . $this->build_path($filename);
    }
    
    /**
     * Build bucket path from filename
     */
    public function build_bucket_path(string $filename): string {
        return SQMedia_Settings::get_current_prefix() . '/' . $this->build_path($filename);
    }
    
    /**
     * Get all files referenced in WordPress metadata
     * Returns array of relative paths (for file existence checking)
     * 
     * @param bool $include_webp Include WebP versions
     * @return array ['master' => path, 'original' => path|null, 'sizes' => [name => path, ...]]
     */
    public function get_all_files(bool $include_webp = true): array {
        $files = [
            'master' => $this->get_master_path(),
            'original' => null,
            'sizes' => [],
        ];
        
        // Original image (if scaled)
        if ($this->has_original_image()) {
            $files['original'] = $this->build_path($this->get_original_image());
        }
        
        // All sizes from WordPress metadata
        $sizes = $include_webp ? $this->get_wp_sizes() : $this->get_wp_image_sizes();
        
        foreach ($sizes as $size_name => $size_data) {
            if (!empty($size_data['file'])) {
                $files['sizes'][$size_name] = $this->build_path($size_data['file']);
            }
        }
        
        return $files;
    }
    
    // =========================================================================
    // PERSISTENCE
    // =========================================================================
    
    /**
     * Save record to database
     */
    public function save(): bool {
        if (!$this->is_loaded()) {
            return false;
        }
        
        $db = sqmedia()->get_database();
        
        return $db->update_record($this->get_attachment_id(), [
            'storage_location' => $this->get_storage_location(),
            'has_webp' => $this->has_webp() ? 1 : 0,
            'status' => $this->get_status(),
            'last_verified' => current_time('mysql'),
        ]);
    }
    
    /**
     * Delete record from database
     */
    public function delete(): bool {
        if (!$this->is_loaded()) {
            return false;
        }
        
        $db = sqmedia()->get_database();
        return $db->delete_record($this->get_attachment_id());
    }
    
    /**
     * Refresh record from database
     */
    public function refresh(): self {
        if ($this->is_loaded()) {
            $db = sqmedia()->get_database();
            $this->data = $db->get_record($this->get_attachment_id());
            $this->wp_metadata = null; // Clear cache
        }
        return $this;
    }
    
    /**
     * Convert to array
     */
    public function to_array(): array {
        if (!$this->is_loaded()) {
            return [];
        }
        
        return [
            'id' => $this->get_id(),
            'attachment_id' => $this->get_attachment_id(),
            'storage_location' => $this->get_storage_location(),
            'has_webp' => $this->has_webp(),
            'master_path' => $this->get_master_path(),
            'master_dir' => $this->get_master_dir(),
            'master_filename' => $this->get_master_filename(),
            'original_image' => $this->get_original_image(),
            'original_location' => $this->get_original_location(),
            'status' => $this->get_status(),
            'batch_id' => $this->get_batch_id(),
            'registered_via' => $this->get_registered_via(),
            'registered_at' => $this->get_registered_at(),
            'last_verified' => $this->get_last_verified(),
        ];
    }
}