# Generated by hand on 2026-01-02
# Migrate ArchiveResult from integer PK to UUID PK (matching Snapshot)

from django.db import migrations, models, connection
from uuid import UUID
from archivebox.uuid_compat import uuid7


def migrate_archiveresult_id_to_uuid(apps, schema_editor):
    """
    Migrate ArchiveResult from integer PK to UUID PK (clean one-step migration).

    Handles both migration paths:
    - 0.7.x: ArchiveResult has integer id, NO uuid field → generate new UUIDs
    - 0.8.x: ArchiveResult has integer id + optional uuid field → reuse existing UUIDs

    Strategy:
    1. Create new table with UUID as primary key (no temporary columns)
    2. Generate UUIDs for records missing them (0.7.x) or reuse existing (0.8.x)
    3. Copy all data with UUID as new id
    4. Drop old table, rename new table
    5. Recreate indexes

    Result: Clean schema with ONLY id as UUIDField (no old_id, no uuid)
    """
    cursor = connection.cursor()

    # Check if table exists and has data
    cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='core_archiveresult'")
    if not cursor.fetchone():
        print('ArchiveResult table does not exist, skipping migration')
        return

    cursor.execute("SELECT COUNT(*) FROM core_archiveresult")
    row_count = cursor.fetchone()[0]

    # Don't skip if table is empty - we still need to recreate to remove uuid column
    # (fresh installs create table with uuid from 0025, but model expects no uuid after 0029)

    if row_count == 0:
        print('[0029] Recreating ArchiveResult table schema (integer→UUID PK, removing uuid column)...')
    else:
        print(f'[0029] Migrating {row_count} ArchiveResult records from integer PK to UUID PK...')

    # Step 0: Check if machine_process table exists, if not NULL out process_id values
    cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='machine_process'")
    machine_process_exists = cursor.fetchone() is not None

    if not machine_process_exists:
        print('machine_process table does not exist yet, setting process_id to NULL')
        cursor.execute("UPDATE core_archiveresult SET process_id = NULL WHERE process_id IS NOT NULL")

    # Step 1: Create new table with UUID as primary key (clean - no old_id or uuid columns)
    cursor.execute("""
        CREATE TABLE core_archiveresult_new (
            id TEXT PRIMARY KEY NOT NULL,
            created_at DATETIME NOT NULL,
            modified_at DATETIME NOT NULL,

            snapshot_id TEXT NOT NULL,
            plugin VARCHAR(32) NOT NULL,
            hook_name VARCHAR(255) NOT NULL DEFAULT '',

            status VARCHAR(15) NOT NULL DEFAULT 'queued',
            retry_at DATETIME,

            start_ts DATETIME,
            end_ts DATETIME,

            output_str TEXT NOT NULL DEFAULT '',
            output_json TEXT,
            output_files TEXT NOT NULL DEFAULT '{}',
            output_size BIGINT NOT NULL DEFAULT 0,
            output_mimetypes VARCHAR(512) NOT NULL DEFAULT '',

            config TEXT NOT NULL DEFAULT '{}',
            notes TEXT NOT NULL DEFAULT '',
            num_uses_succeeded INTEGER NOT NULL DEFAULT 0,
            num_uses_failed INTEGER NOT NULL DEFAULT 0,

            process_id TEXT,

            FOREIGN KEY (snapshot_id) REFERENCES core_snapshot(id) ON DELETE CASCADE,
            FOREIGN KEY (process_id) REFERENCES machine_process(id) ON DELETE SET NULL
        );
    """)

    # Step 2: Generate UUIDs for records that don't have them
    # Check if uuid column exists (0.8.x has it, 0.7.x doesn't)
    cursor.execute("PRAGMA table_info(core_archiveresult)")
    columns = cursor.fetchall()
    col_names = [col[1] for col in columns]
    has_uuid_column = 'uuid' in col_names

    if has_uuid_column:
        cursor.execute("SELECT id, uuid FROM core_archiveresult")
        records = cursor.fetchall()
        id_to_uuid = {}
        for old_id, existing_uuid in records:
            if existing_uuid:
                # Normalize existing UUID to 32-char hex format (Django SQLite UUIDField format)
                # (existing UUIDs might be stored with or without dashes in old schema)
                id_to_uuid[old_id] = UUID(existing_uuid).hex
            else:
                # Generate new UUIDv7 (time-ordered) as 32-char hex
                id_to_uuid[old_id] = uuid7().hex
    else:
        # 0.7.x path: no uuid column, generate new UUIDs for all records
        cursor.execute("SELECT id FROM core_archiveresult")
        records = cursor.fetchall()
        id_to_uuid = {old_id: uuid7().hex for (old_id,) in records}

    # Step 3: Copy data with UUIDs as new primary key
    cursor.execute("SELECT * FROM core_archiveresult")
    old_records = cursor.fetchall()

    # col_names already fetched in Step 2
    inserted_count = 0
    for i, record in enumerate(old_records):
        old_id = record[col_names.index('id')]
        new_uuid = id_to_uuid[old_id]

        # Build insert with new structure
        values = {col_names[i]: record[i] for i in range(len(col_names))}

        # List of fields to copy (all fields from new schema except id, old_id, uuid)
        fields_to_copy = [
            'created_at', 'modified_at', 'snapshot_id', 'plugin', 'hook_name',
            'status', 'retry_at', 'start_ts', 'end_ts',
            'output_str', 'output_json', 'output_files', 'output_size', 'output_mimetypes',
            'config', 'notes', 'num_uses_succeeded', 'num_uses_failed', 'process_id'
        ]

        # Build INSERT statement (only copy fields that exist in source)
        existing_fields = [f for f in fields_to_copy if f in values]

        if i == 0:
            print(f'[0029] Source columns: {col_names}')
            print(f'[0029] Copying fields: {existing_fields}')

        placeholders = ', '.join(['?'] * (len(existing_fields) + 1))  # +1 for id
        field_list = 'id, ' + ', '.join(existing_fields)

        insert_values = [new_uuid] + [values.get(f) for f in existing_fields]

        try:
            cursor.execute(
                f"INSERT INTO core_archiveresult_new ({field_list}) VALUES ({placeholders})",
                insert_values
            )
            inserted_count += 1
        except Exception as e:
            print(f'[0029] ERROR inserting record {old_id}: {e}')
            if i == 0:
                print(f'[0029] First record values: {insert_values[:5]}...')
                raise

    print(f'[0029] Inserted {inserted_count}/{len(old_records)} records')

    # Step 4: Replace old table with new table
    cursor.execute("DROP TABLE core_archiveresult")
    cursor.execute("ALTER TABLE core_archiveresult_new RENAME TO core_archiveresult")

    # Step 5: Create indexes
    cursor.execute("CREATE INDEX core_archiveresult_snapshot_id_idx ON core_archiveresult(snapshot_id)")
    cursor.execute("CREATE INDEX core_archiveresult_plugin_idx ON core_archiveresult(plugin)")
    cursor.execute("CREATE INDEX core_archiveresult_status_idx ON core_archiveresult(status)")
    cursor.execute("CREATE INDEX core_archiveresult_retry_at_idx ON core_archiveresult(retry_at)")
    cursor.execute("CREATE INDEX core_archiveresult_created_at_idx ON core_archiveresult(created_at)")
    cursor.execute("CREATE INDEX core_archiveresult_hook_name_idx ON core_archiveresult(hook_name)")
    cursor.execute("CREATE INDEX core_archiveresult_process_id_idx ON core_archiveresult(process_id)")

    print(f'✓ Migrated {row_count} ArchiveResult records to UUID primary key')


class Migration(migrations.Migration):

    dependencies = [
        ('core', '0028_alter_snapshot_fs_version'),
    ]

    operations = [
        migrations.SeparateDatabaseAndState(
            database_operations=[
                migrations.RunPython(
                    migrate_archiveresult_id_to_uuid,
                    reverse_code=migrations.RunPython.noop,
                ),
            ],
            state_operations=[
                # Remove uuid field (was added in 0025, we're merging it into id)
                migrations.RemoveField(
                    model_name='archiveresult',
                    name='uuid',
                ),
                # Change id from AutoField to UUIDField (absorbing the uuid field)
                migrations.AlterField(
                    model_name='archiveresult',
                    name='id',
                    field=models.UUIDField(primary_key=True, default=uuid7, editable=False, unique=True),
                ),
            ],
        ),
    ]
