<?php
declare(strict_types=1);

namespace Itdatex\Rechnungseingang;

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

/**
 * Datei-Layer mit SHA-256-Hashkette.
 *
 * Verantwortlich fuer:
 *   - Speichern der Datei mit Zufallsnamen auf der Platte (Original-Name nur in DB)
 *   - Berechnen des Datei-SHA-256
 *   - Verketten mit dem chain_hash der vorherigen Zeile
 *   - Pruefen, ob eine Datei bereits existiert (Dubletten via UNIQUE KEY uq_sha)
 *
 * Die Tabelle selbst wird via InvoiceRepository::insertFile geschrieben — Storage
 * gibt nur das vorbereitete Datensatz-Array zurueck (Single Responsibility).
 */
final class Storage
{
    /**
     * @return array{stored_name:string, original_name:string, mime:string, size_bytes:int, sha256:string, chain_hash:string, full_path:string}
     */
    public function persist( string $sourcePath, string $originalName, string $previousChainHash ): array
    {
        if ( ! is_file( $sourcePath ) || ! is_readable( $sourcePath ) ) {
            throw new \RuntimeException( "Datei nicht lesbar: {$sourcePath}" );
        }

        $sha256 = hash_file( 'sha256', $sourcePath );
        if ( ! is_string( $sha256 ) || strlen( $sha256 ) !== 64 ) {
            throw new \RuntimeException( 'SHA-256 konnte nicht berechnet werden.' );
        }

        $dir = self::dir();
        if ( ! is_dir( $dir ) || ! is_writable( $dir ) ) {
            throw new \RuntimeException( "Upload-Ordner nicht beschreibbar: {$dir}" );
        }

        // Extension aus dem Originalnamen extrahieren — beschraenkt auf bekannte Formate
        $ext = self::safeExtension( $originalName );

        // Zufallsname
        $storedName = bin2hex( random_bytes( 16 ) ) . ( $ext ? ".{$ext}" : '' );
        $fullPath   = trailingslashit( $dir ) . $storedName;

        if ( ! copy( $sourcePath, $fullPath ) ) {
            throw new \RuntimeException( "Datei konnte nicht gespeichert werden: {$fullPath}" );
        }
        @chmod( $fullPath, 0640 );

        $mime = self::detectMime( $fullPath );
        $size = (int) filesize( $fullPath );

        // Hashkette: SHA256( previousChainHash . currentSha256 )
        // Erste Datei: previousChainHash = 64x '0'
        $prev = $previousChainHash !== '' ? $previousChainHash : str_repeat( '0', 64 );
        $chainHash = hash( 'sha256', $prev . $sha256 );

        return [
            'stored_name'   => $storedName,
            'original_name' => $originalName,
            'mime'          => $mime,
            'size_bytes'    => $size,
            'sha256'        => $sha256,
            'chain_hash'    => $chainHash,
            'full_path'     => $fullPath,
        ];
    }

    public function fullPath( string $storedName ): string
    {
        return trailingslashit( self::dir() ) . $storedName;
    }

    public static function dir(): string
    {
        $dir = (string) get_option( Installer::UPLOAD_DIR_OPTION, '' );
        if ( $dir !== '' ) return $dir;

        // Fallback: berechnen wie im Installer (falls die Option noch nicht gesetzt ist)
        $uploads = wp_upload_dir();
        return trailingslashit( $uploads['basedir'] ) . 'itdatex-e-invoice-intake-fallback';
    }

    private static function safeExtension( string $original ): string
    {
        $ext = strtolower( (string) pathinfo( $original, PATHINFO_EXTENSION ) );
        return in_array( $ext, [ 'xml', 'pdf' ], true ) ? $ext : '';
    }

    private static function detectMime( string $path ): string
    {
        if ( function_exists( 'finfo_open' ) ) {
            $f = finfo_open( FILEINFO_MIME_TYPE );
            if ( $f !== false ) {
                $mime = (string) finfo_file( $f, $path );
                finfo_close( $f );
                if ( $mime !== '' ) return $mime;
            }
        }
        return 'application/octet-stream';
    }
}
