<?php
/**
 * Build bounded sparse structure maps for prepared media signing.
 *
 * @package EncypherProvenance
 */

namespace EncypherProvenance;

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

class HashMediaPlanner
{
    private const MAX_STRUCTURE_BYTES = 33_554_432;
    private const MAX_CHUNKS = 8192;
    private const MAX_PARSE_ITEMS = 8192;
    private const C2PA_UUID_HEX = 'd8fec3d61b0e483c92975828877ec481';
    private const JUMBF_STORE_UUID_HEX = '6332706100110010800000aa00389b71';
    private const JUMBF_MANIFEST_UUID_HEX = '63326d6100110010800000aa00389b71';
    private const JPEG_MIMES = ['image/jpeg'];
    private const RIFF_MIMES = ['audio/wav', 'image/webp', 'video/x-msvideo'];
    private const TIFF_MIMES = ['image/tiff', 'image/x-adobe-dng'];
    private const BMFF_MIMES = [
        'application/mp4',
        'audio/mp4',
        'image/avif',
        'image/heic',
        'image/heic-sequence',
        'image/heif',
        'image/heif-sequence',
        'video/mp4',
        'video/quicktime',
        'video/x-m4v',
    ];
    private const BMFF_CONTAINER_BOX_TYPES = [
        'moov',
        'trak',
        'mdia',
        'minf',
        'stbl',
        'edts',
        'dinf',
        'udta',
        'meta',
        'iprp',
        'ipco',
        'iref',
        'traf',
        'mfra',
    ];
    private const BMFF_FULL_BOX_TYPES = ['meta', 'iref'];
    private const BMFF_TOP_LEVEL_CONTAINER_BOX_TYPES = ['moov', 'moof', 'meta'];
    private const BMFF_OFFSET_BOX_TYPES = ['stco', 'co64', 'iloc', 'tfhd'];

    public static function supports(string $mime): bool
    {
        return in_array(self::canonical_mime($mime), self::supported_mimes(), true);
    }

    public static function binding_algorithm_for_mime(string $mime): ?string
    {
        $mime = self::canonical_mime($mime);
        if (in_array($mime, self::BMFF_MIMES, true)) {
            return 'c2pa.hash.bmff.v3';
        }
        return in_array($mime, self::supported_mimes(), true) ? 'c2pa.hash.data' : null;
    }

    /** @return array<int,array{offset:int,bytes:string}>|\WP_Error */
    public static function bmff_offset_boxes($handle, int $total_size)
    {
        if (! is_resource($handle) || $total_size <= 0) {
            return new \WP_Error('structure_invalid', 'The local BMFF file cannot be planned for hash signing.');
        }
        $ranges = [];
        $offset_ranges = [];
        $parse_items = 0;
        try {
            $error = self::plan_bmff($handle, $total_size, $ranges, $parse_items, $offset_ranges);
        } catch (\OverflowException $error) {
            return new \WP_Error('structure_too_large', 'The media structure contains too many regions for hash signing.');
        }
        if (is_wp_error($error)) {
            return $error;
        }
        if (count($offset_ranges) > self::MAX_CHUNKS) {
            return new \WP_Error('structure_too_large', 'The media structure contains too many offset tables for hash signing.');
        }
        $total = 0;
        $boxes = [];
        foreach ($offset_ranges as [$offset, $length]) {
            $total += $length;
            if ($total > self::MAX_STRUCTURE_BYTES) {
                return new \WP_Error('structure_too_large', 'The media offset tables exceed the 32 MiB hash-signing limit.');
            }
            $bytes = self::read_at($handle, $offset, $length);
            if (is_wp_error($bytes)) {
                return $bytes;
            }
            $boxes[] = ['offset' => $offset, 'bytes' => $bytes];
        }
        return $boxes;
    }

    /** @return array<int,array{offset:int,b64:string}>|\WP_Error */
    public static function plan($handle, int $total_size, string $mime)
    {
        if (! is_resource($handle) || $total_size <= 0) {
            return new \WP_Error('structure_invalid', 'The local media file cannot be planned for hash signing.');
        }

        $ranges = [];
        $parse_items = 0;
        $mime = self::canonical_mime($mime);
        try {
            if (in_array($mime, self::JPEG_MIMES, true)) {
                $error = self::plan_jpeg($handle, $total_size, $ranges, $parse_items);
            } elseif ('image/png' === $mime) {
                $error = self::plan_png($handle, $total_size, $ranges, $parse_items);
            } elseif (in_array($mime, self::RIFF_MIMES, true)) {
                $error = self::plan_riff($handle, $total_size, $ranges, $parse_items);
            } elseif (in_array($mime, self::BMFF_MIMES, true)) {
                $error = self::plan_bmff($handle, $total_size, $ranges, $parse_items);
            } elseif (in_array($mime, self::TIFF_MIMES, true)) {
                $error = self::plan_tiff($handle, $total_size, $ranges, $parse_items);
            } elseif ('image/gif' === $mime) {
                $error = self::plan_gif($handle, $total_size, $ranges, $parse_items);
            } elseif ('image/svg+xml' === $mime) {
                $error = self::plan_svg($handle, $total_size, $ranges);
            } elseif ('audio/mpeg' === $mime) {
                $error = self::plan_id3($handle, $total_size, $ranges, $parse_items);
            } elseif ('audio/flac' === $mime) {
                $error = self::plan_flac($handle, $total_size, $ranges, $parse_items);
            } elseif ('audio/ogg' === $mime) {
                $error = self::plan_ogg($handle, $total_size, $ranges, $parse_items);
            } elseif ('image/jxl' === $mime) {
                $error = self::plan_jxl($handle, $total_size, $ranges, $parse_items);
            } else {
                return new \WP_Error('hash_mode_unsupported', 'This media format does not support sparse hash signing.');
            }
        } catch (\OverflowException $error) {
            return new \WP_Error('structure_too_large', 'The media structure contains too many regions for hash signing.');
        }
        if (is_wp_error($error)) {
            return $error;
        }

        $ranges = self::merge_ranges($ranges);
        if (count($ranges) > self::MAX_CHUNKS) {
            return new \WP_Error('structure_too_large', 'The media structure contains too many regions for hash signing.');
        }

        $total = 0;
        $chunks = [];
        foreach ($ranges as [$offset, $length]) {
            $total += $length;
            if ($total > self::MAX_STRUCTURE_BYTES) {
                return new \WP_Error('structure_too_large', 'The media structure exceeds the 32 MiB hash-signing limit.');
            }
            $bytes = self::read_at($handle, $offset, $length);
            if (is_wp_error($bytes)) {
                return $bytes;
            }
            $chunks[] = ['offset' => $offset, 'b64' => base64_encode($bytes)];
        }
        return $chunks;
    }

    private static function plan_jpeg($handle, int $size, array &$ranges, int &$parse_items)
    {
        $geometry = self::jpeg_geometry($handle, $size, $parse_items);
        if (is_wp_error($geometry)) {
            return $geometry;
        }
        foreach (array_merge($geometry['structure'], $geometry['deletes']) as [$offset, $length]) {
            self::add_range($ranges, $offset, $length, $size);
        }
        return true;
    }

    /**
     * Parse the JPEG marker table and identify only complete C2PA APP11 families.
     *
     * @return array{insert_at:int,deletes:array<int,array{0:int,1:int}>,structure:array<int,array{0:int,1:int}>}|\WP_Error
     */
    public static function jpeg_geometry($handle, int $size, ?int &$parse_items = null)
    {
        $magic = self::read_at($handle, 0, 2);
        if (is_wp_error($magic) || "\xff\xd8" !== $magic) {
            return new \WP_Error('structure_invalid', 'The JPEG start marker is invalid.');
        }
        $items = null === $parse_items ? 0 : $parse_items;
        self::consume_parse_item($items);
        $structure = [[0, 2]];
        $packets = [];
        $candidate_bytes = 0;
        $position = 2;
        $insert_at = 2;
        while ($position + 2 <= $size) {
            self::consume_parse_item($items);
            $marker = self::read_at($handle, $position, 2);
            if (is_wp_error($marker) || "\xff" !== $marker[0]) {
                return new \WP_Error('structure_invalid', 'The JPEG marker table is invalid.');
            }
            $code = ord($marker[1]);
            $structure[] = [$position, 2];
            if (0xda === $code || 0xd9 === $code) {
                if (null !== $parse_items) {
                    $parse_items = $items;
                }
                return [
                    'insert_at' => $insert_at,
                    'deletes' => self::valid_jpeg_family_ranges($packets),
                    'structure' => $structure,
                ];
            }
            if (0x01 === $code || ($code >= 0xd0 && $code <= 0xd7)) {
                $position += 2;
                continue;
            }
            $length_bytes = self::read_at($handle, $position + 2, 2);
            if (is_wp_error($length_bytes)) {
                return $length_bytes;
            }
            $length = unpack('nlength', $length_bytes)['length'];
            $end = $position + 2 + $length;
            if ($length < 2 || $end <= $position || $end > $size) {
                return new \WP_Error('structure_invalid', 'A JPEG segment is out of bounds.');
            }
            $structure[] = [$position + 2, 2];
            if (2 === $position && 0xe0 === $code) {
                $insert_at = $end;
            }
            if (0xeb === $code && $length >= 4) {
                $identifier = self::read_at($handle, $position + 4, 2);
                if (! is_wp_error($identifier) && 'JP' === $identifier) {
                    $candidate_bytes += $end - $position;
                    if ($candidate_bytes > self::MAX_STRUCTURE_BYTES) {
                        return new \WP_Error('structure_too_large', 'The JPEG APP11 candidates exceed the 32 MiB hash-signing limit.');
                    }
                    $segment = self::read_at($handle, $position, $end - $position);
                    if (is_wp_error($segment)) {
                        return $segment;
                    }
                    $packet = self::jpeg_packet($segment, $position, $end);
                    if (null !== $packet) {
                        $packets[] = $packet;
                    }
                }
            }
            $position = $end;
        }
        return new \WP_Error('structure_invalid', 'The JPEG scan marker is missing.');
    }

    /** @return array<string,mixed>|null */
    private static function jpeg_packet(string $segment, int $start, int $end): ?array
    {
        if (
            strlen($segment) < 20
            || "\xff\xeb" !== substr($segment, 0, 2)
            || 'JP' !== substr($segment, 4, 2)
        ) {
            return null;
        }
        $declared = unpack('nlength', substr($segment, 2, 2))['length'];
        $lbox = unpack('Nlength', substr($segment, 12, 4))['length'];
        $header_length = 1 === $lbox ? 16 : 8;
        if (
            $declared + 2 !== strlen($segment)
            || strlen($segment) < 12 + $header_length
            || 'jumb' !== substr($segment, 16, 4)
        ) {
            return null;
        }
        return [
            'start' => $start,
            'end' => $end,
            'en' => unpack('nvalue', substr($segment, 6, 2))['value'],
            'z' => unpack('Nvalue', substr($segment, 8, 4))['value'],
            'header' => substr($segment, 12, $header_length),
            'dbox' => substr($segment, 12 + $header_length),
        ];
    }

    /** @param array<int,array<string,mixed>> $packets @return array<int,array{0:int,1:int}> */
    private static function valid_jpeg_family_ranges(array $packets): array
    {
        $ranges = [];
        $family = [];
        $finish = static function (array $candidate) use (&$ranges): void {
            if ([] === $candidate) {
                return;
            }
            $first = $candidate[0];
            $store = $first['header'];
            foreach ($candidate as $index => $packet) {
                if (
                    $packet['z'] !== $index + 1
                    || $packet['header'] !== $first['header']
                    || (0 < $index && $candidate[$index - 1]['end'] !== $packet['start'])
                ) {
                    return;
                }
                $store .= $packet['dbox'];
            }
            if (self::is_complete_manifest_store($store)) {
                $last = $candidate[count($candidate) - 1];
                $ranges[] = [$first['start'], $last['end'] - $first['start']];
            }
        };
        foreach ($packets as $packet) {
            $previous = [] === $family ? null : $family[count($family) - 1];
            $continues = null !== $previous
                && $previous['end'] === $packet['start']
                && $previous['en'] === $packet['en']
                && $previous['z'] + 1 === $packet['z']
                && $previous['header'] === $packet['header'];
            if ([] !== $family && ! $continues) {
                $finish($family);
                $family = [];
            }
            $family[] = $packet;
        }
        $finish($family);
        return $ranges;
    }

    public static function is_complete_manifest_store(string $store): bool
    {
        $parsed = self::parse_jumbf_superbox($store);
        return null !== $parsed
            && self::JUMBF_STORE_UUID_HEX === $parsed['uuid']
            && in_array(self::JUMBF_MANIFEST_UUID_HEX, $parsed['children'], true);
    }

    public static function bmff_uuid_payload_is_valid(string $payload): bool
    {
        $length = strlen($payload);
        for ($position = 4; $position + 4 <= $length; ++$position) {
            if ('jumb' !== substr($payload, $position, 4)) {
                continue;
            }
            $store = substr($payload, $position - 4);
            if (self::is_complete_manifest_store($store)) {
                return true;
            }
        }
        return false;
    }

    /** @return array{uuid:string,children:array<int,string>}|null */
    private static function parse_jumbf_superbox(string $bytes): ?array
    {
        $outer = self::jumbf_box_at($bytes, 0, strlen($bytes));
        if (null === $outer || 'jumb' !== $outer['type'] || $outer['end'] !== strlen($bytes)) {
            return null;
        }
        $description = self::jumbf_box_at($bytes, $outer['payload'], $outer['end']);
        if (
            null === $description
            || 'jumd' !== $description['type']
            || $description['payload'] + 16 > $description['end']
        ) {
            return null;
        }
        $uuid = bin2hex(substr($bytes, $description['payload'], 16));
        $children = [];
        $position = $description['end'];
        while ($position < $outer['end']) {
            $child = self::jumbf_box_at($bytes, $position, $outer['end']);
            if (null === $child || $child['end'] <= $position) {
                return null;
            }
            if ('jumb' === $child['type']) {
                $nested = self::parse_jumbf_superbox(substr($bytes, $position, $child['end'] - $position));
                if (null === $nested) {
                    return null;
                }
                $children[] = $nested['uuid'];
            }
            $position = $child['end'];
        }
        return ['uuid' => $uuid, 'children' => $children];
    }

    /** @return array{type:string,payload:int,end:int}|null */
    private static function jumbf_box_at(string $bytes, int $offset, int $limit): ?array
    {
        if ($offset < 0 || $offset + 8 > $limit || $limit > strlen($bytes)) {
            return null;
        }
        $size = unpack('Nsize', substr($bytes, $offset, 4))['size'];
        $type = substr($bytes, $offset + 4, 4);
        $header = 8;
        if (1 === $size) {
            if ($offset + 16 > $limit) {
                return null;
            }
            $parts = unpack('Nhigh/Nlow', substr($bytes, $offset + 8, 8));
            if ($parts['high'] > 0x7fffffff) {
                return null;
            }
            $size = ($parts['high'] * 4294967296) + $parts['low'];
            $header = 16;
        } elseif (0 === $size) {
            $size = $limit - $offset;
        }
        $end = $offset + $size;
        if ($size < $header || $end <= $offset || $end > $limit) {
            return null;
        }
        return ['type' => $type, 'payload' => $offset + $header, 'end' => $end];
    }

    /** @return bool|\WP_Error */
    public static function store_payload_is_complete($handle, int $offset, int $length)
    {
        if ($length <= 0 || $length > self::MAX_STRUCTURE_BYTES) {
            return false;
        }
        $store = self::read_at($handle, $offset, $length);
        return is_wp_error($store) ? $store : self::is_complete_manifest_store($store);
    }

    /** @return true|\WP_Error */
    public static function validate_store_payload($handle, int $offset, int $length, string $container)
    {
        if ($length <= 0) {
            return new \WP_Error('structure_invalid', "The {$container} C2PA carrier is empty.");
        }
        if ($length > self::MAX_STRUCTURE_BYTES) {
            return new \WP_Error('structure_too_large', "The {$container} C2PA carrier exceeds the hash-signing limit.");
        }
        $store = self::read_at($handle, $offset, $length);
        if (is_wp_error($store)) {
            return $store;
        }
        return self::is_complete_manifest_store($store)
            ? true
            : new \WP_Error('structure_invalid', "The {$container} C2PA carrier is not a complete JUMBF manifest store.");
    }

    /** @return true|\WP_Error */
    public static function validate_bmff_uuid_box($handle, int $payload_start, int $end)
    {
        $length = $end - $payload_start - 16;
        if ($length <= 0) {
            return new \WP_Error('structure_invalid', 'The BMFF C2PA UUID payload is empty.');
        }
        if ($length > self::MAX_STRUCTURE_BYTES) {
            return new \WP_Error('structure_too_large', 'The BMFF C2PA carrier exceeds the hash-signing limit.');
        }
        $payload = self::read_at($handle, $payload_start + 16, $length);
        if (is_wp_error($payload)) {
            return $payload;
        }
        return self::bmff_uuid_payload_is_valid($payload)
            ? true
            : new \WP_Error('structure_invalid', 'The BMFF C2PA UUID payload is not a complete JUMBF manifest store.');
    }

    private static function plan_png($handle, int $size, array &$ranges, int &$parse_items)
    {
        $signature = self::read_at($handle, 0, 8);
        if (is_wp_error($signature) || "\x89PNG\r\n\x1a\n" !== $signature) {
            return new \WP_Error('structure_invalid', 'The PNG signature is invalid.');
        }
        self::add_range($ranges, 0, 8, $size);
        $position = 8;
        while ($position + 12 <= $size) {
            self::consume_parse_item($parse_items);
            $header = self::read_at($handle, $position, 8);
            if (is_wp_error($header)) {
                return $header;
            }
            $length = unpack('Nlength', substr($header, 0, 4))['length'];
            $end = $position + 12 + $length;
            if ($end > $size) {
                return new \WP_Error('structure_invalid', 'A PNG chunk is out of bounds.');
            }
            self::add_range($ranges, $position, 8, $size);
            $kind = substr($header, 4, 4);
            if ('IHDR' === $kind) {
                self::add_range($ranges, $position, 12 + $length, $size);
            }
            if ('caBX' === $kind) {
                $valid = self::validate_store_payload($handle, $position + 8, $length, 'PNG');
                if (is_wp_error($valid)) {
                    return $valid;
                }
                self::add_range($ranges, $position, 12 + $length, $size);
            }
            if ('IEND' === $kind) {
                if (0 !== $length || $end !== $size) {
                    return new \WP_Error('structure_invalid', 'The PNG end chunk must be empty and terminal.');
                }
                return true;
            }
            $position = $end;
        }
        return new \WP_Error('structure_invalid', 'The PNG end chunk is missing.');
    }

    private static function plan_riff($handle, int $size, array &$ranges, int &$parse_items)
    {
        $header = self::read_at($handle, 0, 12);
        if (is_wp_error($header) || 'RIFF' !== substr($header, 0, 4)) {
            return new \WP_Error('structure_invalid', 'The RIFF header is invalid.');
        }
        if (unpack('Vsize', substr($header, 4, 4))['size'] + 8 !== $size) {
            return new \WP_Error('structure_invalid', 'The RIFF declared size does not match the local file.');
        }
        self::add_range($ranges, 0, 12, $size);
        $position = 12;
        while ($position + 8 <= $size) {
            self::consume_parse_item($parse_items);
            $chunk = self::read_at($handle, $position, 8);
            if (is_wp_error($chunk)) {
                return $chunk;
            }
            $length = unpack('Vlength', substr($chunk, 4, 4))['length'];
            $padded = $length + ($length & 1);
            $end = $position + 8 + $padded;
            if ($end > $size) {
                return new \WP_Error('structure_invalid', 'A RIFF chunk is out of bounds.');
            }
            self::add_range($ranges, $position, 8, $size);
            if ('C2PA' === substr($chunk, 0, 4)) {
                $valid = self::validate_store_payload($handle, $position + 8, $length, 'RIFF');
                if (is_wp_error($valid)) {
                    return $valid;
                }
                self::add_range($ranges, $position, 8 + $padded, $size);
            }
            $position = $end;
        }
        return $position === $size
            ? true
            : new \WP_Error('structure_invalid', 'The RIFF chunk table has trailing bytes.');
    }
    private static function plan_svg($handle, int $size, array &$ranges)
    {
        $locations = self::scan_svg_markup($handle, $size);
        if (is_wp_error($locations)) {
            return $locations;
        }
        if (null === $locations['root_end']) {
            return new \WP_Error('structure_invalid', 'The SVG root element is missing.');
        }
        if ($locations['root_end'] > self::MAX_STRUCTURE_BYTES) {
            return new \WP_Error('structure_too_large', 'The SVG root prefix exceeds the 32 MiB structural-read limit.');
        }
        self::add_range($ranges, 0, $locations['root_end'], $size);

        $carrier_count = count($locations['element']) + count($locations['pi']);
        if ($carrier_count > 1) {
            return new \WP_Error('structure_invalid', 'The SVG contains multiple C2PA manifest carriers.');
        }
        if (0 === $carrier_count) {
            return true;
        }

        if ([] !== $locations['element']) {
            $open = $locations['element'][0];
            $close = null;
            foreach ($locations['close'] as $candidate) {
                if ($candidate[0] >= $open[1]) {
                    $close = $candidate;
                    break;
                }
            }
            if (null === $close) {
                return $size - $open[0] >= self::MAX_STRUCTURE_BYTES
                    ? new \WP_Error('structure_too_large', 'The SVG manifest carrier exceeds the 32 MiB structural-read limit.')
                    : new \WP_Error('structure_invalid', 'The SVG manifest element is unterminated.');
            }
            $start = $open[0];
            $end = $close[1];
            $metadata_open = '<metadata>';
            if ($start >= strlen($metadata_open)) {
                $before = self::read_at($handle, $start - strlen($metadata_open), strlen($metadata_open));
                if (is_wp_error($before)) {
                    return $before;
                }
                if ($metadata_open === $before) {
                    $start -= strlen($metadata_open);
                }
            }
            $metadata_close = '</metadata>';
            if ($end + strlen($metadata_close) <= $size) {
                $after = self::read_at($handle, $end, strlen($metadata_close));
                if (is_wp_error($after)) {
                    return $after;
                }
                if ($metadata_close === $after) {
                    $end += strlen($metadata_close);
                }
            }
        } else {
            [$start, $end] = $locations['pi'][0];
        }
        if ($end - $start > self::MAX_STRUCTURE_BYTES) {
            return new \WP_Error('structure_too_large', 'The SVG manifest carrier exceeds the 32 MiB structural-read limit.');
        }
        self::add_range($ranges, $start, $end - $start, $size);
        return true;
    }

    /**
     * Scan XML markup without retaining SVG document payload bytes.
     *
     * @return array{root_end:?int,element:array<int,array{0:int,1:int}>,close:array<int,array{0:int,1:int}>,pi:array<int,array{0:int,1:int}>}|\WP_Error
     */
    private static function scan_svg_markup($handle, int $size)
    {
        $found = ['root_end' => null, 'element' => [], 'close' => [], 'pi' => []];
        $state = 'text';
        $start = 0;
        $name = '';
        $closing = false;
        $pending = null;
        $quote = null;
        $probe = '';
        $marker = '';
        $declaration_depth = 0;
        $pi_prefix = '';
        $pi_target = '<?c2pa-manifest ';
        $previous = '';
        $offset = 0;
        if (0 !== fseek($handle, 0, SEEK_SET)) {
            return new \WP_Error('media_read_failed', 'Could not seek within the local media file.');
        }
        while ($offset < $size) {
            $part = fread($handle, min(1_048_576, $size - $offset)); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fread -- Lexical streaming keeps private SVG payload out of memory and off device.
            if (false === $part || '' === $part) {
                return new \WP_Error('media_read_failed', 'Could not read the local media structure.');
            }
            $part_length = strlen($part);
            for ($index = 0; $index < $part_length; ++$index) {
                $char = $part[$index];
                $position = $offset + $index;
                if ('text' === $state) {
                    if ('<' === $char) {
                        $state = 'after_lt';
                        $start = $position;
                    }
                    continue;
                }
                if ('after_lt' === $state) {
                    if ('!' === $char) {
                        $state = 'bang';
                        $probe = '<!';
                    } elseif ('?' === $char) {
                        $state = 'pi';
                        $pi_prefix = '<?';
                        $previous = '';
                    } elseif ('/' === $char) {
                        $state = 'name';
                        $name = '';
                        $closing = true;
                    } else {
                        $state = 'name';
                        $name = $char;
                        $closing = false;
                    }
                    continue;
                }
                if ('bang' === $state) {
                    $probe .= $char;
                    if ('<!--' === $probe) {
                        $state = 'comment';
                        $marker = '';
                    } elseif ('<![CDATA[' === $probe) {
                        $state = 'cdata';
                        $marker = '';
                    } elseif (! str_starts_with('<!--', $probe) && ! str_starts_with('<![CDATA[', $probe)) {
                        $state = 'declaration';
                        $quote = null;
                        $declaration_depth = substr_count($probe, '[') - substr_count($probe, ']');
                        if ('>' === $char && 0 === $declaration_depth) {
                            $state = 'text';
                        }
                    }
                    continue;
                }
                if ('comment' === $state || 'cdata' === $state) {
                    $marker = substr($marker . $char, -3);
                    $terminator = 'comment' === $state ? '-->' : ']]>';
                    if ($terminator === $marker) {
                        $state = 'text';
                    }
                    continue;
                }
                if ('declaration' === $state) {
                    if (null !== $quote) {
                        if ($quote === $char) {
                            $quote = null;
                        }
                    } elseif ('"' === $char || "'" === $char) {
                        $quote = $char;
                    } elseif ('[' === $char) {
                        ++$declaration_depth;
                    } elseif (']' === $char && 0 < $declaration_depth) {
                        --$declaration_depth;
                    } elseif ('>' === $char && 0 === $declaration_depth) {
                        $state = 'text';
                    }
                    continue;
                }
                if ('pi' === $state) {
                    if (strlen($pi_prefix) < strlen($pi_target)) {
                        $pi_prefix .= $char;
                    }
                    if ('?' === $previous && '>' === $char) {
                        if ($pi_target === $pi_prefix) {
                            $found['pi'][] = [$start, $position + 1];
                        }
                        $state = 'text';
                    }
                    $previous = $char;
                    continue;
                }
                if ('name' === $state) {
                    if ('>' !== $char && '/' !== $char && ! ctype_space($char)) {
                        $name .= $char;
                        continue;
                    }
                    $pending = null;
                    if ($closing && 'c2pa:manifest' === $name && '>' === $char) {
                        $found['close'][] = [$start, $position + 1];
                    } elseif (! $closing && 'svg' === $name) {
                        $pending = 'root';
                    } elseif (! $closing && 'c2pa:manifest' === $name) {
                        $pending = 'element';
                    }
                    if ('>' === $char) {
                        if ('root' === $pending && null === $found['root_end']) {
                            $found['root_end'] = $position + 1;
                        } elseif ('element' === $pending) {
                            $found['element'][] = [$start, $position + 1];
                        }
                        $state = 'text';
                    } else {
                        $state = 'tag';
                        $quote = null;
                    }
                    continue;
                }
                if ('tag' === $state) {
                    if (null !== $quote) {
                        if ($quote === $char) {
                            $quote = null;
                        }
                    } elseif ('"' === $char || "'" === $char) {
                        $quote = $char;
                    } elseif ('>' === $char) {
                        if ('root' === $pending && null === $found['root_end']) {
                            $found['root_end'] = $position + 1;
                        } elseif ('element' === $pending) {
                            $found['element'][] = [$start, $position + 1];
                        }
                        $state = 'text';
                    }
                }
            }
            $offset += $part_length;
        }
        if ('text' !== $state) {
            return new \WP_Error('structure_invalid', 'The SVG markup is unterminated.');
        }
        return $found;
    }

    /** @return int|false|\WP_Error */
    private static function stream_find($handle, string $needle, int $start, int $end)
    {
        if ($start < 0 || $start > $end || $end < 0) {
            return new \WP_Error('structure_invalid', 'The streamed structure offsets are invalid.');
        }
        if (0 !== fseek($handle, $start, SEEK_SET)) {
            return new \WP_Error('media_read_failed', 'Could not seek within the local media file.');
        }
        $tail = '';
        $offset = $start;
        $overlap = strlen($needle) - 1;
        while ($offset < $end) {
            $part = fread($handle, min(1_048_576, $end - $offset)); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fread -- Bounded streaming search avoids retaining private SVG payload.
            if (false === $part || '' === $part) {
                return new \WP_Error('media_read_failed', 'Could not read the local media structure.');
            }
            $buffer = $tail . $part;
            $position = strpos($buffer, $needle);
            if (false !== $position) {
                return $offset - strlen($tail) + $position;
            }
            $offset += strlen($part);
            $tail = 0 < $overlap ? substr($buffer, -$overlap) : '';
        }
        return false;
    }

    private static function plan_id3(
        $handle,
        int $size,
        array &$ranges,
        int &$parse_items,
        ?int &$tag_length = null
    )
    {
        $prefix = self::read_at($handle, 0, min(10, $size));
        if (is_wp_error($prefix)) {
            return $prefix;
        }
        if ($size < 3 || 'ID3' !== substr($prefix, 0, 3)) {
            $tag_length = 0;
            self::add_range($ranges, 0, min(3, $size), $size);
            return true;
        }
        if ($size < 10) {
            return new \WP_Error('structure_invalid', 'The ID3 header is truncated.');
        }

        $major = ord($prefix[3]);
        if (! in_array($major, [3, 4], true)) {
            return new \WP_Error('structure_invalid', 'Only ID3v2.3 and ID3v2.4 tags are supported.');
        }
        if (0 !== (ord($prefix[5]) & 0x80)) {
            return new \WP_Error('structure_invalid', 'ID3 global unsynchronisation is not supported.');
        }
        if (0 !== (ord($prefix[5]) & 0x40)) {
            return new \WP_Error('structure_invalid', 'ID3 extended headers are not supported.');
        }
        $tag_size = self::synchsafe_id3_size(substr($prefix, 6, 4));
        if (is_wp_error($tag_size)) {
            return $tag_size;
        }
        $body_end = 10 + $tag_size;
        $footer = 4 === $major && 0 !== (ord($prefix[5]) & 0x10) ? 10 : 0;
        $tag_length = $body_end + $footer;
        if ($tag_length > $size) {
            return new \WP_Error('structure_invalid', 'The ID3 tag is out of bounds.');
        }

        self::add_range($ranges, 0, 10, $size);
        $position = 10;
        $c2pa_geob = 0;
        while ($position < $body_end) {
            self::consume_parse_item($parse_items);
            $first = self::read_at($handle, $position, 1);
            if (is_wp_error($first)) {
                return $first;
            }
            if ("\0" === $first) {
                self::add_range($ranges, $position, 1, $size);
                break;
            }
            if ($position + 10 > $body_end) {
                return new \WP_Error('structure_invalid', 'The ID3 frame header is truncated.');
            }
            $header = self::read_at($handle, $position, 10);
            if (is_wp_error($header)) {
                return $header;
            }
            self::add_range($ranges, $position, 10, $size);
            if (4 === $major) {
                $frame_size = self::synchsafe_id3_size(substr($header, 4, 4));
                if (is_wp_error($frame_size)) {
                    return $frame_size;
                }
            } else {
                $frame_size = unpack('Nsize', substr($header, 4, 4))['size'];
            }
            $frame_end = $position + 10 + $frame_size;
            if ($frame_end > $body_end) {
                return new \WP_Error('structure_invalid', 'An ID3 frame is out of bounds.');
            }
            if ('GEOB' === substr($header, 0, 4)) {
                if (0 === $frame_size) {
                    return new \WP_Error('structure_invalid', 'An ID3 GEOB frame is empty.');
                }
                $classification_length = min(18, $frame_size);
                self::add_range($ranges, $position + 10, $classification_length, $size);
                $classification = self::read_at($handle, $position + 10, $classification_length);
                if (is_wp_error($classification)) {
                    return $classification;
                }
                if ("\0application/c2pa\0" === $classification) {
                    ++$c2pa_geob;
                    if ($c2pa_geob > 1) {
                        return new \WP_Error('structure_invalid', 'The ID3 tag contains multiple C2PA GEOB frames.');
                    }
                    self::add_range($ranges, $position, 10 + $frame_size, $size);
                }
            }
            $position = $frame_end;
        }
        return true;
    }

    /** @return int|\WP_Error */
    private static function synchsafe_id3_size(string $bytes)
    {
        if (4 !== strlen($bytes)) {
            return new \WP_Error('structure_invalid', 'The ID3 size is truncated.');
        }
        $size = 0;
        for ($index = 0; $index < 4; ++$index) {
            $byte = ord($bytes[$index]);
            if (0 !== ($byte & 0x80)) {
                return new \WP_Error('structure_invalid', 'The ID3 size is not synchsafe.');
            }
            $size = ($size << 7) | $byte;
        }
        return $size;
    }

    private static function plan_flac($handle, int $size, array &$ranges, int &$parse_items)
    {
        $id3_length = 0;
        $error = self::plan_id3($handle, $size, $ranges, $parse_items, $id3_length);
        if (is_wp_error($error)) {
            return $error;
        }
        $magic = self::read_at($handle, $id3_length, 4);
        if (is_wp_error($magic) || 'fLaC' !== $magic) {
            return new \WP_Error('structure_invalid', 'The FLAC signature is invalid.');
        }
        self::add_range($ranges, $id3_length, 4, $size);
        $position = $id3_length + 4;
        do {
            self::consume_parse_item($parse_items);
            $header = self::read_at($handle, $position, 4);
            if (is_wp_error($header)) {
                return $header;
            }
            $last = 0 !== (ord($header[0]) & 0x80);
            $kind = ord($header[0]) & 0x7f;
            $length = (ord($header[1]) << 16) | (ord($header[2]) << 8) | ord($header[3]);
            $end = $position + 4 + $length;
            if ($end > $size) {
                return new \WP_Error('structure_invalid', 'A FLAC metadata block is out of bounds.');
            }
            self::add_range($ranges, $position, 4, $size);
            if (2 === $kind && $length >= 4) {
                $application = self::read_at($handle, $position + 4, 4);
                if (! is_wp_error($application) && 'c2pa' === $application) {
                    self::add_range($ranges, $position, 4 + $length, $size);
                } else {
                    self::add_range($ranges, $position + 4, 4, $size);
                }
            }
            $position = $end;
        } while (! $last);
        return true;
    }

    private static function plan_tiff($handle, int $size, array &$ranges, int &$parse_items)
    {
        $header = self::read_at($handle, 0, 8);
        if (is_wp_error($header)) {
            return $header;
        }
        $little = 'II' === substr($header, 0, 2);
        if (! $little && 'MM' !== substr($header, 0, 2)) {
            return new \WP_Error('structure_invalid', 'The TIFF byte order is invalid.');
        }
        $u16 = static fn(string $bytes): int => unpack($little ? 'v' : 'n', $bytes)[1];
        $u32 = static fn(string $bytes): int => unpack($little ? 'V' : 'N', $bytes)[1];
        if (42 !== $u16(substr($header, 2, 2))) {
            return new \WP_Error('structure_invalid', 'The TIFF magic is invalid.');
        }
        self::add_range($ranges, 0, 8, $size);
        $ifd = $u32(substr($header, 4, 4));
        $visited = [];
        while (0 !== $ifd) {
            self::consume_parse_item($parse_items);
            if (isset($visited[$ifd]) || count($visited) >= 64) {
                return new \WP_Error('structure_invalid', 'The TIFF IFD chain is cyclic.');
            }
            $visited[$ifd] = true;
            $count_bytes = self::read_at($handle, $ifd, 2);
            if (is_wp_error($count_bytes)) {
                return $count_bytes;
            }
            $count = $u16($count_bytes);
            $length = 2 + ($count * 12) + 4;
            if ($ifd + $length > $size) {
                return new \WP_Error('structure_invalid', 'A TIFF IFD is out of bounds.');
            }
            $table = self::read_at($handle, $ifd, $length);
            if (is_wp_error($table)) {
                return $table;
            }
            self::add_range($ranges, $ifd, $length, $size);
            for ($index = 0; $index < $count; ++$index) {
                $entry = substr($table, 2 + ($index * 12), 12);
                if (0xcd41 === $u16(substr($entry, 0, 2))) {
                    $store_length = $u32(substr($entry, 4, 4));
                    if ($store_length > 4) {
                        self::add_range($ranges, $u32(substr($entry, 8, 4)), $store_length, $size);
                    }
                }
            }
            $ifd = $u32(substr($table, $length - 4, 4));
        }
        return true;
    }

    private static function plan_gif($handle, int $size, array &$ranges, int &$parse_items)
    {
        $header = self::read_at($handle, 0, 13);
        if (is_wp_error($header) || ! in_array(substr($header, 0, 6), ['GIF87a', 'GIF89a'], true)) {
            return new \WP_Error('structure_invalid', 'The GIF signature is invalid.');
        }
        self::add_range($ranges, 0, 13, $size);
        $position = 13;
        if (0 !== (ord($header[10]) & 0x80)) {
            $length = 3 * (1 << ((ord($header[10]) & 7) + 1));
            self::add_range($ranges, $position, $length, $size);
            $position += $length;
        }
        while ($position < $size) {
            self::consume_parse_item($parse_items);
            $kind = self::read_at($handle, $position, 1);
            if (is_wp_error($kind)) {
                return $kind;
            }
            if ("\x3b" === $kind) {
                self::add_range($ranges, $position, 1, $size);
                return $position + 1 === $size
                    ? true
                    : new \WP_Error('structure_invalid', 'Bytes follow the GIF trailer.');
            }
            if ("\x21" === $kind) {
                $prefix = self::read_at($handle, $position, 3);
                if (is_wp_error($prefix)) {
                    return $prefix;
                }
                $label = ord($prefix[1]);
                if (in_array($label, [0xff, 0x01], true)) {
                    self::add_range($ranges, $position, 3 + ord($prefix[2]), $size);
                    $position += 3 + ord($prefix[2]);
                } elseif (0xf9 === $label) {
                    self::add_range($ranges, $position, 8, $size);
                    $position += 8;
                    continue;
                } else {
                    self::add_range($ranges, $position, 2, $size);
                    $position += 2;
                }
            } elseif ("\x2c" === $kind) {
                $descriptor = self::read_at($handle, $position, 10);
                if (is_wp_error($descriptor)) {
                    return $descriptor;
                }
                self::add_range($ranges, $position, 10, $size);
                $position += 10;
                if (0 !== (ord($descriptor[9]) & 0x80)) {
                    $table_length = 3 * (1 << ((ord($descriptor[9]) & 7) + 1));
                    self::add_range($ranges, $position, $table_length, $size);
                    $position += $table_length;
                }
                self::add_range($ranges, $position, 1, $size);
                ++$position;
            } else {
                return new \WP_Error('structure_invalid', 'The GIF block table is invalid.');
            }
            do {
                $length_byte = self::read_at($handle, $position, 1);
                if (is_wp_error($length_byte)) {
                    return $length_byte;
                }
                self::add_range($ranges, $position, 1, $size);
                $block_length = ord($length_byte);
                $position += 1 + $block_length;
                if ($position > $size) {
                    return new \WP_Error('structure_invalid', 'A GIF sub-block is out of bounds.');
                }
            } while (0 !== $block_length);
        }
        return new \WP_Error('structure_invalid', 'The GIF trailer is missing.');
    }

    private static function plan_jxl($handle, int $size, array &$ranges, int &$parse_items)
    {
        $position = 0;
        $first = true;
        while ($position < $size) {
            self::consume_parse_item($parse_items);
            $box = self::box_header($handle, $position, $size);
            if (is_wp_error($box)) {
                return $box;
            }
            if ($first && 'JXL ' !== $box['type']) {
                return new \WP_Error('structure_invalid', 'The JXL signature box is missing.');
            }
            self::add_range($ranges, $position, $box['header'], $size);
            if ('jumb' === $box['type']) {
                $is_store = self::store_payload_is_complete($handle, $position, $box['end'] - $position);
                if (is_wp_error($is_store)) {
                    return $is_store;
                }
                if ($is_store) {
                    self::add_range($ranges, $position, $box['end'] - $position, $size);
                } else {
                    $description_start = $position + $box['header'];
                    $description = self::box_header($handle, $description_start, $box['end']);
                    if (
                        ! is_wp_error($description)
                        && 'jumd' === $description['type']
                        && $description_start + $description['header'] + 16 <= $description['end']
                    ) {
                        self::add_range($ranges, $description_start, $description['header'] + 16, $size);
                    }
                }
            }
            $first = false;
            $position = $box['end'];
        }
        return true;
    }

    private static function plan_ogg($handle, int $size, array &$ranges, int &$parse_items)
    {
        $parsed = self::parse_ogg_pages($handle, $size, $parse_items);
        if (is_wp_error($parsed)) {
            return $parsed;
        }

        $pages = $parsed['pages'];
        $manifest_serial = $parsed['manifest_serial'];
        $prefix_remaining = [];
        foreach ($pages as $page) {
            self::add_range($ranges, $page['start'], $page['body'] - $page['start'], $size);
            $serial = $page['serial'];
            if ($serial === $manifest_serial) {
                self::add_range($ranges, $page['body'], $page['end'] - $page['body'], $size);
                continue;
            }
            if (! array_key_exists($serial, $prefix_remaining)) {
                $prefix_remaining[$serial] = 5;
            }
            if (0 === $prefix_remaining[$serial]) {
                continue;
            }
            $body_position = $page['body'];
            for ($index = 0, $count = strlen($page['laces']); $index < $count; ++$index) {
                $lace = ord($page['laces'][$index]);
                $take = min($prefix_remaining[$serial], $lace);
                if ($take > 0) {
                    self::add_range($ranges, $body_position, $take, $size);
                    $prefix_remaining[$serial] -= $take;
                }
                $body_position += $lace;
                if ($lace < 255) {
                    $prefix_remaining[$serial] = 0;
                    break;
                }
            }
        }
        return true;
    }

    /**
     * Parse and locally validate every Ogg page and logical stream.
     *
     * Full page bytes are read only for CRC validation. The returned sparse
     * records contain framing and first-packet identity, never media payload.
     *
     * @return array{pages:array<int,array<string,mixed>>,manifest_serial:?int}|\WP_Error
     */
    private static function parse_ogg_pages($handle, int $size, int &$parse_items)
    {
        $pages = [];
        $streams = [];
        $position = 0;
        while ($position < $size) {
            self::consume_parse_item($parse_items);
            $fixed = self::read_at($handle, $position, 27);
            if (is_wp_error($fixed) || 'OggS' !== substr($fixed, 0, 4) || "\0" !== $fixed[4]) {
                return new \WP_Error('structure_invalid', 'An Ogg page header is invalid.');
            }
            $header_type = ord($fixed[5]);
            if (0 !== ($header_type & ~0x07)) {
                return new \WP_Error('structure_invalid', 'An Ogg page has invalid flags.');
            }
            $lace_count = ord($fixed[26]);
            if (0 === $lace_count) {
                return new \WP_Error('structure_invalid', 'An Ogg page has no lacing values.');
            }
            $laces = self::read_at($handle, $position + 27, $lace_count);
            if (is_wp_error($laces)) {
                return new \WP_Error('structure_invalid', 'An Ogg page lacing table is truncated.');
            }
            $body_length = 0;
            for ($index = 0; $index < $lace_count; ++$index) {
                $body_length += ord($laces[$index]);
            }
            $body = $position + 27 + $lace_count;
            $end = $body + $body_length;
            if ($end > $size) {
                return new \WP_Error('structure_invalid', 'An Ogg page is out of bounds.');
            }
            $page_bytes = self::read_at($handle, $position, $end - $position);
            if (is_wp_error($page_bytes)) {
                return new \WP_Error('structure_invalid', 'An Ogg page is truncated.');
            }
            $declared_crc = unpack('V', substr($page_bytes, 22, 4))[1];
            $page_bytes = substr_replace($page_bytes, "\0\0\0\0", 22, 4);
            if ($declared_crc !== self::ogg_crc($page_bytes)) {
                return new \WP_Error('structure_invalid', 'An Ogg page checksum is invalid.');
            }

            $serial = unpack('V', substr($fixed, 14, 4))[1];
            $sequence = unpack('V', substr($fixed, 18, 4))[1];
            if (! isset($streams[$serial])) {
                if (0 !== $sequence || 0 === ($header_type & 0x02) || 0 !== ($header_type & 0x01)) {
                    return new \WP_Error('structure_invalid', 'An Ogg logical stream has an invalid first page.');
                }
                $streams[$serial] = [
                    'last_sequence' => null,
                    'packet_open' => false,
                    'packet_count' => 0,
                    'first_complete' => false,
                    'first_prefix' => '',
                    'eos' => false,
                ];
            } else {
                $previous = $streams[$serial]['last_sequence'];
                if (0xffffffff === $previous || $sequence !== $previous + 1) {
                    return new \WP_Error('structure_invalid', 'An Ogg logical stream page sequence is invalid.');
                }
                if ($streams[$serial]['eos']) {
                    return new \WP_Error('structure_invalid', 'An Ogg page follows the end of its logical stream.');
                }
                if (0 !== ($header_type & 0x02)) {
                    return new \WP_Error('structure_invalid', 'A later Ogg page repeats the BOS flag.');
                }
                if ((0 !== ($header_type & 0x01)) !== $streams[$serial]['packet_open']) {
                    return new \WP_Error('structure_invalid', 'An Ogg packet continuation flag is invalid.');
                }
            }

            $body_position = $body;
            for ($index = 0; $index < $lace_count; ++$index) {
                $lace = ord($laces[$index]);
                if (! $streams[$serial]['first_complete'] && strlen($streams[$serial]['first_prefix']) < 5) {
                    $take = min(5 - strlen($streams[$serial]['first_prefix']), $lace);
                    if ($take > 0) {
                        $part = self::read_at($handle, $body_position, $take);
                        if (is_wp_error($part)) {
                            return new \WP_Error('structure_invalid', 'An Ogg packet is truncated.');
                        }
                        $streams[$serial]['first_prefix'] .= $part;
                    }
                }
                $body_position += $lace;
                if ($lace < 255) {
                    ++$streams[$serial]['packet_count'];
                    $streams[$serial]['first_complete'] = true;
                    $streams[$serial]['packet_open'] = false;
                } else {
                    $streams[$serial]['packet_open'] = true;
                }
            }
            if (0 !== ($header_type & 0x04)) {
                if ($streams[$serial]['packet_open']) {
                    return new \WP_Error('structure_invalid', 'An Ogg stream ends without a packet terminator.');
                }
                $streams[$serial]['eos'] = true;
            }
            $streams[$serial]['last_sequence'] = $sequence;
            $pages[] = [
                'start' => $position,
                'end' => $end,
                'type' => $header_type,
                'serial' => $serial,
                'laces' => $laces,
                'body' => $body,
                'sequence' => $sequence,
            ];
            $position = $end;
        }
        if ([] === $pages) {
            return new \WP_Error('structure_invalid', 'The Ogg stream is empty.');
        }

        $manifest_serial = null;
        foreach ($streams as $serial => $state) {
            if (! $state['eos']) {
                return new \WP_Error('structure_invalid', 'An Ogg logical stream has no EOS page.');
            }
            if ("\0c2pa" === $state['first_prefix']) {
                if (null !== $manifest_serial) {
                    return new \WP_Error('structure_invalid', 'Multiple Ogg C2PA streams are ambiguous.');
                }
                if (1 !== $state['packet_count']) {
                    return new \WP_Error('structure_invalid', 'Material follows an Ogg C2PA packet.');
                }
                $manifest_serial = (int) $serial;
            }
        }
        return ['pages' => $pages, 'manifest_serial' => $manifest_serial];
    }

    private static function ogg_crc(string $bytes): int
    {
        $crc = 0;
        for ($index = 0, $length = strlen($bytes); $index < $length; ++$index) {
            $crc ^= ord($bytes[$index]) << 24;
            for ($bit = 0; $bit < 8; ++$bit) {
                $crc = 0 !== ($crc & 0x80000000)
                    ? (($crc << 1) ^ 0x04c11db7) & 0xffffffff
                    : ($crc << 1) & 0xffffffff;
            }
        }
        return $crc;
    }


    private static function plan_bmff(
        $handle,
        int $size,
        array &$ranges,
        int &$parse_items,
        ?array &$offset_ranges = null
    ) {
        $position = 0;
        while ($position + 8 <= $size) {
            self::consume_parse_item($parse_items);
            $box = self::box_header($handle, $position, $size);
            if (is_wp_error($box)) {
                return $box;
            }
            self::add_range($ranges, $position, $box['header'], $size);
            if ('uuid' === $box['type']) {
                $uuid = self::read_at($handle, $position + $box['header'], 16);
                if (! is_wp_error($uuid) && self::C2PA_UUID_HEX === bin2hex($uuid)) {
                    $valid = self::validate_bmff_uuid_box(
                        $handle,
                        $position + $box['header'],
                        $box['end']
                    );
                    if (is_wp_error($valid)) {
                        return $valid;
                    }
                    self::add_range($ranges, $position, $box['end'] - $position, $size);
                }
            }
            if (in_array($box['type'], self::BMFF_TOP_LEVEL_CONTAINER_BOX_TYPES, true)) {
                $child_start = $position + $box['header'] + self::bmff_header_skip($box['type']);
                $error = self::walk_bmff_children(
                    $handle,
                    $child_start,
                    $box['end'],
                    $ranges,
                    0,
                    $size,
                    $parse_items,
                    $offset_ranges
                );
                if (is_wp_error($error)) {
                    return $error;
                }
            }
            $position = $box['end'];
        }
        return $position === $size ? true : new \WP_Error('structure_invalid', 'The BMFF top-level box table is invalid.');
    }

    private static function walk_bmff_children(
        $handle,
        int $start,
        int $end,
        array &$ranges,
        int $depth,
        int $file_size,
        int &$parse_items,
        ?array &$offset_ranges = null
    )
    {
        if ($depth > 16 || $start > $end) {
            return new \WP_Error('structure_invalid', 'The BMFF box nesting is invalid.');
        }
        $position = $start;
        while ($position + 8 <= $end) {
            self::consume_parse_item($parse_items);
            $box = self::box_header($handle, $position, $end);
            if (is_wp_error($box)) {
                return $box;
            }
            if (in_array($box['type'], self::BMFF_CONTAINER_BOX_TYPES, true)) {
                $ancestor_length = $box['header'] + self::bmff_header_skip($box['type']);
                self::add_range($ranges, $position, $ancestor_length, $file_size);
            }
            if (in_array($box['type'], self::BMFF_OFFSET_BOX_TYPES, true)) {
                self::add_range($ranges, $position, $box['end'] - $position, $file_size);
                if (null !== $offset_ranges) {
                    self::add_range($offset_ranges, $position, $box['end'] - $position, $file_size);
                }
            } elseif (in_array($box['type'], self::BMFF_CONTAINER_BOX_TYPES, true)) {
                $skip = self::bmff_header_skip($box['type']);
                $error = self::walk_bmff_children(
                    $handle,
                    $position + $box['header'] + $skip,
                    $box['end'],
                    $ranges,
                    $depth + 1,
                    $file_size,
                    $parse_items,
                    $offset_ranges
                );
                if (is_wp_error($error)) {
                    return $error;
                }
            }
            $position = $box['end'];
        }
        return true;
    }

    /** @return array{type:string,header:int,end:int}|\WP_Error */
    private static function box_header($handle, int $offset, int $limit)
    {
        $header = self::read_at($handle, $offset, 8);
        if (is_wp_error($header)) {
            return $header;
        }
        $size = unpack('Nsize', substr($header, 0, 4))['size'];
        $type = substr($header, 4, 4);
        $header_length = 8;
        if (1 === $size) {
            $large = self::read_at($handle, $offset + 8, 8);
            if (is_wp_error($large)) {
                return $large;
            }
            $parts = unpack('Nhigh/Nlow', $large);
            if ($parts['high'] > 0x7fffffff) {
                return new \WP_Error('structure_invalid', 'A BMFF box is too large for this platform.');
            }
            $size = ($parts['high'] * 4294967296) + $parts['low'];
            $header_length = 16;
        } elseif (0 === $size) {
            $size = $limit - $offset;
        }
        $end = $offset + $size;
        if ($size < $header_length || $end > $limit) {
            return new \WP_Error('structure_invalid', 'A BMFF box is out of bounds.');
        }
        return ['type' => $type, 'header' => $header_length, 'end' => $end];
    }

    /** @return array<int,string> */
    private static function supported_mimes(): array
    {
        static $supported = null;
        if (null !== $supported) {
            return $supported;
        }

        $json = file_get_contents(__DIR__ . '/hash-mode-formats.json');
        $artifact = false !== $json ? json_decode($json, true) : null;
        if (
            ! is_array($artifact)
            || 'encypher.hash-mode-formats.v1' !== ($artifact['schema'] ?? null)
            || ! isset($artifact['mime_types'])
            || ! is_array($artifact['mime_types'])
        ) {
            $supported = [];
            return $supported;
        }

        $supported = array_values(array_filter(
            $artifact['mime_types'],
            static fn($mime): bool => is_string($mime) && '' !== $mime
        ));
        return $supported;
    }

    public static function canonical_mime(string $mime): string
    {
        $base = strtolower(trim(explode(';', $mime, 2)[0]));
        return match ($base) {
            'image/jpg' => 'image/jpeg',
            'audio/aac' => 'audio/mp4',
            'audio/mpa' => 'audio/mpeg',
            'audio/x-flac' => 'audio/flac',
            'application/ogg' => 'audio/ogg',
            'audio/wave', 'audio/vnd.wave', 'audio/x-wav' => 'audio/wav',
            'image/dng' => 'image/x-adobe-dng',
            'application/svg+xml' => 'image/svg+xml',
            'video/avi', 'video/msvideo', 'application/x-troff-msvideo' => 'video/x-msvideo',
            default => $base,
        };
    }

    private static function bmff_header_skip(string $box_type): int
    {
        return in_array($box_type, self::BMFF_FULL_BOX_TYPES, true) ? 4 : 0;
    }

    private static function consume_parse_item(int &$parse_items): void
    {
        if ($parse_items >= self::MAX_PARSE_ITEMS) {
            throw new \OverflowException('The media structure contains too many parse items.');
        }
        ++$parse_items;
    }

    private static function add_range(array &$ranges, int $offset, int $length, int $size): void
    {
        if ($offset < 0 || $length <= 0 || $offset + $length > $size) {
            return;
        }
        // Bound parser allocations before overlapping ranges are merged.
        if (count($ranges) >= self::MAX_CHUNKS) {
            throw new \OverflowException('The media structure contains too many raw ranges.');
        }
        $ranges[] = [$offset, $length];
    }

    /** @return array<int,array{0:int,1:int}> */
    private static function merge_ranges(array $ranges): array
    {
        usort($ranges, static fn(array $a, array $b): int => $a[0] <=> $b[0]);
        $merged = [];
        foreach ($ranges as [$offset, $length]) {
            if ([] === $merged) {
                $merged[] = [$offset, $length];
                continue;
            }
            $last = count($merged) - 1;
            $end = $merged[$last][0] + $merged[$last][1];
            if ($offset < $end) {
                $merged[$last][1] = max($end, $offset + $length) - $merged[$last][0];
            } else {
                $merged[] = [$offset, $length];
            }
        }
        return $merged;
    }

    /** @return string|\WP_Error */
    private static function read_at($handle, int $offset, int $length)
    {
        if ($length < 0 || 0 !== fseek($handle, $offset, SEEK_SET)) {
            return new \WP_Error('media_read_failed', 'Could not seek within the local media file.');
        }
        $bytes = '';
        while (strlen($bytes) < $length) {
            $part = fread($handle, min(1_048_576, $length - strlen($bytes))); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fread -- Bounded structural reads require the locked seekable media stream; WP_Filesystem cannot seek.
            if (false === $part || '' === $part) {
                return new \WP_Error('media_read_failed', 'Could not read the local media structure.');
            }
            $bytes .= $part;
        }
        return $bytes;
    }
}
