<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany; // Aggiunto per condomini()
use Illuminate\Database\Eloquent\SoftDeletes; // Aggiunto per soft deletes
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\DB;

class Amministratore extends Model
{
    use HasFactory;

    /**
     * The table associated with the model.
     *
     * @var string
     */
    protected $table = 'amministratori';
    // Rimossa la primaryKey personalizzata - usa 'id' standard Laravel    
    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    
    protected $fillable = [
        'user_id',
        'nome',
        'cognome',
        'denominazione_studio',
        'partita_iva',
        'codice_fiscale_studio',
        'indirizzo_studio',
        'cap_studio',
        'citta_studio',
        'provincia_studio',
        'telefono_studio',
        'email_studio',
        'pec_studio',
        'codice_amministratore',
    ];

    /**
     * Get the user associated with the Amministratore.
     */
    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class, 'user_id');
    }

    /**
     * Get the condomini for the Amministratore.
     */
    public function stabili(): HasMany
    {
        return $this->hasMany(Stabile::class, 'amministratore_id', 'id');
    }
    /**
     * Get the fornitori for the Amministratore.
     */
    public function fornitori(): HasMany
    {
        return $this->hasMany(Fornitore::class, 'amministratore_id', 'id');
    }

    protected static function boot()
    {
        parent::boot();

        static::creating(function ($amministratore) {
            if (empty($amministratore->codice_amministratore)) {
                do {
                    $code = Str::upper(Str::random(8));
                } while (self::where('codice_amministratore', $code)->exists());
                $amministratore->codice_amministratore = $code;
            }
        });

        // Auto-crea cartelle dopo la creazione
        static::created(function ($amministratore) {
            $amministratore->createFolderStructure();
        });
    }

    /**
     * Crea la struttura di cartelle per l'amministratore
     */
    public function createFolderStructure(): void
    {
        $basePath = "amministratori/{$this->codice_univoco}";
        
        $folders = [
            'documenti/allegati',
            'documenti/contratti', 
            'documenti/assemblee',
            'documenti/preventivi',
            'backup/database',
            'backup/files',
            'temp/upload',
            'temp/processing',
            'logs',
            'exports',
        ];

        foreach ($folders as $folder) {
            Storage::disk('local')->makeDirectory("{$basePath}/{$folder}");
        }

        // Crea file README informativo
        $readme = "# Cartella Amministratore: {$this->nome_completo}\n\n";
        $readme .= "**Codice**: {$this->codice_amministratore}\n";
        $readme .= "**Creato**: " . $this->created_at->format('d/m/Y H:i') . "\n\n";
        $readme .= "## Struttura Cartelle\n\n";
        $readme .= "- `documenti/` - Documenti dell'amministratore\n";
        $readme .= "- `backup/` - Backup automatici\n";
        $readme .= "- `temp/` - File temporanei\n";
        $readme .= "- `logs/` - Log specifici\n";
        $readme .= "- `exports/` - Esportazioni dati\n";

        Storage::disk('local')->put("{$basePath}/README.md", $readme);
    }

    /**
     * Ottieni il path della cartella dell'amministratore
     */
    public function getFolderPath(): string
    {
        return "amministratori/{$this->codice_amministratore}";
    }

    /**
     * Genera codice univoco alfanumerico 8 caratteri
     */
    public function generateCodiceUnivoco(): string
    {
        do {
            $codice = 'ADM' . strtoupper(substr(str_shuffle('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'), 0, 5));
        } while (self::where('codice_amministratore', $codice)->exists());
        
        return $codice;
    }

    /**
     * Verifica se l'amministratore ha un database dedicato attivo
     */
    public function hasDedicatedDatabase(): bool
    {
        return !empty($this->database_attivo);
    }

    /**
     * Ottiene il nome del database dedicato per questo amministratore
     */
    public function getDatabaseName(): string
    {
        return $this->database_attivo ?: "netgescon_{$this->codice_amministratore}";
    }

    /**
     * Ottiene il percorso fisico dell'archivio amministratore
     */
    public function getArchivePath(): string
    {
        return storage_path("app/amministratori/{$this->codice_amministratore}");
    }

    /**
     * Ottiene il percorso del backup database
     */
    public function getDatabaseBackupPath(): string
    {
        return $this->getArchivePath() . '/backup/database';
    }

    /**
     * Verifica se l'archivio fisico esiste
     */
    public function archiveExists(): bool
    {
        return is_dir($this->getArchivePath());
    }

    /**
     * Ottiene informazioni dettagliate sull'archivio
     */
    public function getArchiveInfo(): array
    {
        $archivePath = $this->getArchivePath();
        
        if (!$this->archiveExists()) {
            return [
                'exists' => false,
                'path' => $archivePath,
                'size' => 0,
                'files_count' => 0,
                'last_backup' => null
            ];
        }

        $size = 0;
        $filesCount = 0;
        $lastBackup = null;
        
        // Calcola dimensione totale e numero file
        $iterator = new \RecursiveIteratorIterator(
            new \RecursiveDirectoryIterator($archivePath)
        );
        
        foreach ($iterator as $file) {
            if ($file->isFile()) {
                $size += $file->getSize();
                $filesCount++;
                
                // Trova ultimo backup database
                if (str_contains($file->getPathname(), 'backup/database') && 
                    str_ends_with($file->getFilename(), '.sql')) {
                    $backupTime = filemtime($file->getPathname());
                    if (!$lastBackup || $backupTime > $lastBackup) {
                        $lastBackup = $backupTime;
                    }
                }
            }
        }

        return [
            'exists' => true,
            'path' => $archivePath,
            'size' => $size,
            'size_formatted' => $this->formatBytes($size),
            'files_count' => $filesCount,
            'last_backup' => $lastBackup ? date('Y-m-d H:i:s', $lastBackup) : null,
            'directories' => [
                'documenti' => is_dir($archivePath . '/documenti'),
                'backup' => is_dir($archivePath . '/backup'),
                'temp' => is_dir($archivePath . '/temp'),
                'logs' => is_dir($archivePath . '/logs'),
                'exports' => is_dir($archivePath . '/exports'),
            ]
        ];
    }

    /**
     * Formatta bytes in formato leggibile
     */
    private function formatBytes(int $bytes, int $precision = 2): string
    {
        $units = ['B', 'KB', 'MB', 'GB', 'TB'];
        
        for ($i = 0; $bytes > 1024 && $i < count($units) - 1; $i++) {
            $bytes /= 1024;
        }
        
        return round($bytes, $precision) . ' ' . $units[$i];
    }

    /**
     * Crea backup del database amministratore
     */
    public function createDatabaseBackup(): array
    {
        try {
            if (!$this->hasDedicatedDatabase()) {
                throw new \Exception('Amministratore non ha database dedicato');
            }

            $backupPath = $this->getDatabaseBackupPath();
            if (!is_dir($backupPath)) {
                mkdir($backupPath, 0755, true);
            }

            $filename = "backup_" . $this->codice_amministratore . "_" . date('Y-m-d_H-i-s') . ".sql";
            $fullPath = $backupPath . '/' . $filename;

            $dbName = $this->getDatabaseName();
            $command = sprintf(
                'mysqldump -h %s -u %s -p%s %s > %s',
                env('DB_HOST', '127.0.0.1'),
                env('DB_USERNAME'),
                env('DB_PASSWORD'),
                escapeshellarg($dbName),
                escapeshellarg($fullPath)
            );

            exec($command, $output, $returnCode);

            if ($returnCode !== 0) {
                throw new \Exception('Errore durante backup database: ' . implode("\n", $output));
            }

            return [
                'success' => true,
                'filename' => $filename,
                'path' => $fullPath,
                'size' => filesize($fullPath),
                'created_at' => date('Y-m-d H:i:s')
            ];

        } catch (\Exception $e) {
            return [
                'success' => false,
                'error' => $e->getMessage()
            ];
        }
    }

    /**
     * Prepara archivio per migrazione/trasferimento
     */
    public function prepareForMigration(): array
    {
        try {
            // 1. Backup database
            $dbBackup = $this->createDatabaseBackup();
            if (!$dbBackup['success']) {
                throw new \Exception('Errore backup database: ' . $dbBackup['error']);
            }

            // 2. Crea archivio ZIP dell'intera cartella
            $archivePath = $this->getArchivePath();
            $zipFilename = "migration_" . $this->codice_amministratore . "_" . date('Y-m-d_H-i-s') . ".zip";
            $zipPath = storage_path("app/migrations/{$zipFilename}");

            if (!is_dir(dirname($zipPath))) {
                mkdir(dirname($zipPath), 0755, true);
            }

            $zip = new \ZipArchive();
            if ($zip->open($zipPath, \ZipArchive::CREATE) !== TRUE) {
                throw new \Exception('Impossibile creare archivio ZIP');
            }

            $iterator = new \RecursiveIteratorIterator(
                new \RecursiveDirectoryIterator($archivePath)
            );

            foreach ($iterator as $file) {
                if ($file->isFile()) {
                    $relativePath = str_replace($archivePath . DIRECTORY_SEPARATOR, '', $file->getPathname());
                    $zip->addFile($file->getPathname(), $relativePath);
                }
            }

            $zip->close();

            // 3. Crea file metadata per migrazione
            $metadata = [
                'amministratore' => [
                    'codice' => $this->codice_amministratore,
                    'nome' => $this->nome,
                    'cognome' => $this->cognome,
                    'denominazione_studio' => $this->denominazione_studio,
                    'database_name' => $this->getDatabaseName(),
                ],
                'migration' => [
                    'created_at' => date('Y-m-d H:i:s'),
                    'source_server' => env('APP_URL'),
                    'database_backup' => $dbBackup['filename'],
                    'archive_size' => filesize($zipPath),
                    'files_count' => $this->getArchiveInfo()['files_count'],
                ],
                'requirements' => [
                    'php_version' => PHP_VERSION,
                    'laravel_version' => app()->version(),
                    'mysql_version' => DB::select('SELECT VERSION() as version')[0]->version,
                ]
            ];

            $metadataPath = dirname($zipPath) . "/metadata_{$this->codice_amministratore}.json";
            file_put_contents($metadataPath, json_encode($metadata, JSON_PRETTY_PRINT));

            return [
                'success' => true,
                'zip_file' => $zipPath,
                'metadata_file' => $metadataPath,
                'size' => filesize($zipPath),
                'database_backup' => $dbBackup['filename']
            ];

        } catch (\Exception $e) {
            return [
                'success' => false,
                'error' => $e->getMessage()
            ];
        }
    }

    /**
     * Attributo computed per nome completo
     */
    public function getNomeCompletoAttribute(): string
    {
        return trim($this->nome . ' ' . $this->cognome);
    }
}