<?php
namespace App\Console\Commands;

use App\Models\Artwork;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use Throwable;

/**
 * Import artworks from legacy `wallz` table and attach categories via `connected` table.
 *
 * Usage:
 *  php artisan skinbase:import-legacy-artworks --chunk=500 --dry-run
 *  php artisan skinbase:import-legacy-artworks --artwork-id=69527
 */
class ImportLegacyArtworks extends Command
{
    protected $signature = 'skinbase:import-legacy-artworks
        {--chunk=500 : chunk size for processing}
        {--limit= : maximum number of legacy rows to import}
        {--artwork-id= : import only one legacy wallz row by id}
        {--dry-run : do not persist any changes}
        {--legacy-connection=legacy : name of legacy DB connection}
        {--legacy-table=wallz : legacy artworks table name}
        {--connected-table=connected : legacy artwork->category table}
    ';

    protected $description = 'Import artworks from legacy DB (wallz) into new artworks table';

    private function coerceUnsignedInt(mixed $value, int $default = 0): int
    {
        if ($value === null) {
            return $default;
        }

        if (is_bool($value)) {
            return $value ? 1 : 0;
        }

        if (is_int($value)) {
            return max(0, $value);
        }

        if (is_float($value)) {
            return max(0, (int) $value);
        }

        if (is_string($value)) {
            $trimmed = trim($value);
            if ($trimmed === '') {
                return $default;
            }
            if (is_numeric($trimmed)) {
                return max(0, (int) $trimmed);
            }
        }

        return $default;
    }

    private function coerceString(mixed $value, string $default = ''): string
    {
        if ($value === null) {
            return $default;
        }

        $stringValue = trim((string) $value);

        return $stringValue !== '' ? $stringValue : $default;
    }

    public function handle(): int
    {
        $chunk = (int) $this->option('chunk');
        $limit = $this->option('limit') ? (int) $this->option('limit') : null;
        $artworkId = $this->option('artwork-id') ? (int) $this->option('artwork-id') : null;
        $dryRun = (bool) $this->option('dry-run');
        $legacyConn = $this->option('legacy-connection');
        $legacyTable = $this->option('legacy-table');
        $connectedTable = $this->option('connected-table');

        if ($artworkId !== null && $artworkId <= 0) {
            $this->error('The --artwork-id option must be a positive integer.');

            return self::FAILURE;
        }

        $this->info("Starting import from {$legacyConn}.{$legacyTable} (chunk={$chunk})");

        $query = DB::connection($legacyConn)->table($legacyTable)->orderBy('id');

        if ($artworkId !== null) {
            $this->info("Scoping import to legacy artwork id={$artworkId}");
            $query->where('id', $artworkId);
            $limit = 1;
        }

        $processed = 0;

        $query->chunkById($chunk, function ($rows) use (&$processed, $limit, $dryRun, $legacyConn, $connectedTable) {
            foreach ($rows as $row) {
                if ($limit !== null && $processed >= $limit) {
                    return false; // stop chunking
                }

                $legacyId = $row->id ?? null;

                $title = $row->name ?? $row->title ?? ($row->headline ?? ('legacy-' . ($legacyId ?? Str::random(6))));
                $description = $row->description ?? $row->desc ?? null;

                $slugBase = Str::slug(substr((string) $title, 0, 120));
                // Use cleaned title slug directly. If no title, fallback to artwork-<id|random>.
                $slug = $slugBase ? $slugBase : 'artwork-' . ($legacyId ?? Str::random(8));

                $publishedAt = null;
                if (! empty($row->datum)) {
                    $publishedAt = date('Y-m-d H:i:s', strtotime($row->datum));
                } elseif (! empty($row->created_at)) {
                    $publishedAt = $row->created_at;
                }

                // File mapping — try common legacy fields. Normalize and ensure file_path is not null.
                $rawFileName = $row->pic ?? $row->picture ?? $row->file ?? $row->fname ?? null;
                $fileName = null;
                $filePath = '';
                if (! empty($rawFileName) && trim((string) $rawFileName) !== '') {
                    $fileName = trim((string) $rawFileName);
                    // store legacy path under legacy/ folder, but do not move files here — admin can handle file migration
                    $filePath = 'legacy/uploads/' . ltrim($fileName, '/');
                }

                // derive mime type if missing (use extension mapping), fallback to application/octet-stream
                $mime = $row->mimetype ?? $row->mime ?? null;
                if (empty($mime) && $fileName) {
                    $ext = strtolower(pathinfo((string) $fileName, PATHINFO_EXTENSION));
                    $map = [
                        'jpg' => 'image/jpeg',
                        'jpeg' => 'image/jpeg',
                        'png' => 'image/png',
                        'gif' => 'image/gif',
                        'bmp' => 'image/bmp',
                        'webp' => 'image/webp',
                        'svg' => 'image/svg+xml',
                        'ico' => 'image/x-icon',
                        'zip' => 'application/zip',
                        'pdf' => 'application/pdf',
                    ];

                    $mime = $map[$ext] ?? null;
                }
                if (empty($mime)) {
                    $mime = 'application/octet-stream';
                }

                $data = [
                    'id' => $row->id ?? null,
                    // NOTE: artworks.user_id is NOT NULL (no FK constraint, but column cannot be null)
                    'user_id' => $row->user_id ?? 1,
                    'title' => (string) $title,
                    'slug' => (string) $slug,
                    'description' => $description,
                    'file_name' => $fileName,
                    // ensure non-null file_path to satisfy NOT NULL DB constraints
                    'file_path' => $filePath ?? '',
                    // legacy DB sometimes has no filesize; default to 0 to satisfy NOT NULL
                    'file_size' => isset($row->filesize) && $row->filesize !== null ? (int) $row->filesize : (isset($row->size) && $row->size !== null ? (int) $row->size : 0),
                    'mime_type' => $mime,
                    'width' => $row->width ?? null,
                    'height' => $row->height ?? null,
                    'is_public' => isset($row->visible) ? (bool) $row->visible : true,
                    'is_approved' => isset($row->approved) ? (bool) $row->approved : true,
                    'published_at' => $publishedAt,
                ];

                // Coerce required NOT NULL columns to safe defaults (legacy data can be messy)
                $data['user_id'] = $this->coerceUnsignedInt($data['user_id'], 1);
                $data['file_name'] = $this->coerceString($data['file_name'], 'legacy-' . ($legacyId ?? Str::random(8)));
                $data['file_path'] = $this->coerceString($data['file_path'], 'legacy/uploads/' . $data['file_name']);
                $data['mime_type'] = $this->coerceString($data['mime_type'], 'application/octet-stream');
                $data['file_size'] = $this->coerceUnsignedInt($data['file_size'], 0);
                $data['width'] = $this->coerceUnsignedInt($data['width'], 0);
                $data['height'] = $this->coerceUnsignedInt($data['height'], 0);

                $this->line('Importing legacy id=' . ($legacyId ?? 'unknown') . ' title=' . $data['title']);

                if ($dryRun) {
                    $processed++;
                    continue;
                }

                try {
                    $art = null;

                    DB::connection()->transaction(function () use (&$art, $data, $legacyId, $legacyConn, $connectedTable) {
                        // Preserve the imported slug verbatim. Public artwork URLs include the artwork id.
                        $data['slug'] = Str::limit((string) ($data['slug'] ?: 'artwork'), 160, '');

                        // Preserve legacy primary ID if available and safe to do so.
                        if (! empty($legacyId) && is_numeric($legacyId) && (int) $legacyId > 0) {
                            $preserveId = (int) $legacyId;
                            if (Artwork::where('id', $preserveId)->exists()) {
                                // Avoid overwriting an existing artwork with the same id.
                                throw new \RuntimeException("Artwork with id {$preserveId} already exists; skipping import for this legacy id.");
                            }
                            $data['id'] = $preserveId;
                        }

                        // If we need to preserve the legacy primary id, perform a raw insert
                        // so auto-increment doesn't assign a different id. Otherwise use Eloquent.
                        if (! empty($data['id'])) {
                            $insert = $data;
                            $ts = date('Y-m-d H:i:s');
                            if (! array_key_exists('created_at', $insert)) {
                                $insert['created_at'] = $ts;
                            }
                            if (! array_key_exists('updated_at', $insert)) {
                                $insert['updated_at'] = $ts;
                            }

                            DB::table('artworks')->insert($insert);
                            $art = Artwork::find($insert['id']);
                        } else {
                            $art = Artwork::create($data);
                        }

                        // attach categories if connected table exists
                        if (DB::connection($legacyConn)->getSchemaBuilder()->hasTable($connectedTable)) {
                            // attempt to find category ids from connected table; common column names: wallz_id, art_id, connected_id
                            $rows = DB::connection($legacyConn)->table($connectedTable)
                                ->where(function ($q) use ($legacyId) {
                                    $q->where('wallz_id', $legacyId)
                                      ->orWhere('art_id', $legacyId)
                                      ->orWhere('item_id', $legacyId);
                                })->get();

                            $categoryIds = [];
                            foreach ($rows as $r) {
                                $cid = $r->category_id ?? $r->cat_id ?? $r->category ?? null;
                                if ($cid) {
                                    // try to find matching Category in new DB by id or slug
                                    if (is_numeric($cid) && \App\Models\Category::where('id', $cid)->exists()) {
                                        $categoryIds[] = (int) $cid;
                                    } else {
                                        // maybe legacy stores slug
                                        $cat = \App\Models\Category::where('slug', $cid)->first();
                                        if ($cat) {
                                            $categoryIds[] = $cat->id;
                                        }
                                    }
                                }
                            }

                            if (! empty($categoryIds)) {
                                $art->categories()->syncWithoutDetaching(array_values(array_unique($categoryIds)));
                            }
                        }
                    });

                    // Post-insert verification: if we attempted to preserve the legacy id,
                    // confirm the row exists with that id. Log mapping if preservation failed.
                    if (! empty($legacyId) && is_numeric($legacyId) && (int) $legacyId > 0) {
                        $preserveId = (int) $legacyId;
                        $exists = Artwork::where('id', $preserveId)->exists();
                        if (! $exists) {
                            // If $art was created but with a different id, log mapping for manual reconciliation
                            if ($art instanceof Artwork) {
                                Log::warning('Imported legacy artwork but failed to preserve id', [
                                    'legacy_id' => $preserveId,
                                    'created_id' => $art->id,
                                    'slug' => $art->slug ?? null,
                                ]);
                            } else {
                                Log::warning('Legacy artwork not found after import', ['legacy_id' => $preserveId]);
                            }
                        }
                    }

                    $processed++;
                } catch (Throwable $e) {
                    $this->error('Failed to import legacy id=' . ($legacyId ?? 'unknown') . ': ' . $e->getMessage());
                    Log::error('ImportLegacyArtworks error', [
                        'legacy_id' => $legacyId,
                        'error' => $e->getMessage(),
                        'data' => $data ?? null,
                        'trace' => $e->getTraceAsString(),
                    ]);
                }
            }

            return null;
        }, 'id');

        if ($artworkId !== null && $processed === 0) {
            $this->warn("Legacy artwork id={$artworkId} was not found in {$legacyConn}.{$legacyTable}.");

            return self::FAILURE;
        }

        $this->info('Import complete. Processed: ' . $processed);

        return self::SUCCESS;
    }
}
