"""Initial database schema

Revision ID: 001
Revises:
Create Date: 2024-01-20 10:00:00

"""
from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision: str = '001'
down_revision: Union[str, None] = None
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
    # Users Tabelle - Enums werden automatisch erstellt durch sa.Enum(name='...')
    op.create_table(
        'users',
        sa.Column('id', sa.Integer(), nullable=False),
        sa.Column('username', sa.String(length=50), nullable=False),
        sa.Column('email', sa.String(length=255), nullable=False),
        sa.Column('password_hash', sa.String(length=255), nullable=False),
        sa.Column('role', sa.Enum('ADMIN', 'EDITOR', 'READONLY', name='userrole'), nullable=False),
        sa.Column('is_active', sa.Boolean(), nullable=False),
        sa.Column('is_verified', sa.Boolean(), nullable=False),
        sa.Column('failed_login_attempts', sa.Integer(), nullable=False),
        sa.Column('created_at', sa.DateTime(), nullable=False),
        sa.Column('updated_at', sa.DateTime(), nullable=False),
        sa.PrimaryKeyConstraint('id')
    )
    op.create_index('ix_users_id', 'users', ['id'])
    op.create_index('ix_users_username', 'users', ['username'], unique=True)
    op.create_index('ix_users_email', 'users', ['email'], unique=True)
    op.create_index('ix_users_role', 'users', ['role'])

    # Assets Tabelle
    op.create_table(
        'assets',
        sa.Column('id', sa.Integer(), nullable=False),
        sa.Column('hostname', sa.String(length=255), nullable=False),
        sa.Column('ip_address', sa.String(length=45), nullable=True),
        sa.Column('wazuh_agent_id', sa.String(length=10), nullable=True),
        sa.Column('operating_system', sa.String(length=255), nullable=True),
        sa.Column('os_version', sa.String(length=100), nullable=True),
        sa.Column('kernel_version', sa.String(length=100), nullable=True),
        sa.Column('source', sa.Enum('WAZUH', 'MANUAL', name='assetsource'), nullable=False),
        sa.Column('status', sa.Enum('ACTIVE', 'INACTIVE', 'DECOMMISSIONED', name='assetstatus'), nullable=False),
        sa.Column('last_scan', sa.DateTime(), nullable=True),
        sa.Column('last_seen', sa.DateTime(), nullable=True),
        sa.Column('description', sa.Text(), nullable=True),
        sa.Column('location', sa.String(length=255), nullable=True),
        sa.Column('owner', sa.String(length=255), nullable=True),
        sa.Column('created_at', sa.DateTime(), nullable=False),
        sa.Column('updated_at', sa.DateTime(), nullable=False),
        sa.PrimaryKeyConstraint('id')
    )
    op.create_index('ix_assets_id', 'assets', ['id'])
    op.create_index('ix_assets_hostname', 'assets', ['hostname'])
    op.create_index('ix_assets_ip_address', 'assets', ['ip_address'])
    op.create_index('ix_assets_wazuh_agent_id', 'assets', ['wazuh_agent_id'], unique=True)
    op.create_index('ix_assets_source', 'assets', ['source'])
    op.create_index('ix_assets_status', 'assets', ['status'])

    # Vulnerabilities Tabelle
    op.create_table(
        'vulnerabilities',
        sa.Column('id', sa.Integer(), nullable=False),
        sa.Column('cve_id', sa.String(length=20), nullable=False),
        sa.Column('asset_id', sa.Integer(), nullable=False),
        sa.Column('cvss_score', sa.Float(), nullable=True),
        sa.Column('cvss_vector', sa.String(length=100), nullable=True),
        sa.Column('severity', sa.Enum('CRITICAL', 'HIGH', 'MEDIUM', 'LOW', 'NONE', name='vulnerabilityseverity'), nullable=False),
        sa.Column('status', sa.Enum('OPEN', 'PATCHED', 'PENDING_VERIFICATION', 'PATCH_FAILED', 'ACCEPTED_RISK', 'FALSE_POSITIVE', name='vulnerabilitystatus'), nullable=False),
        sa.Column('title', sa.Text(), nullable=True),
        sa.Column('description', sa.Text(), nullable=True),
        sa.Column('package_name', sa.String(length=255), nullable=True),
        sa.Column('package_version', sa.String(length=100), nullable=True),
        sa.Column('fixed_version', sa.String(length=100), nullable=True),
        sa.Column('exploitable', sa.Boolean(), nullable=False),
        sa.Column('exploit_available', sa.Boolean(), nullable=False),
        sa.Column('exploit_maturity', sa.String(length=50), nullable=True),
        sa.Column('published_date', sa.DateTime(), nullable=True),
        sa.Column('detected_at', sa.DateTime(), nullable=False),
        sa.Column('patched_at', sa.DateTime(), nullable=True),
        sa.Column('references', sa.Text(), nullable=True),
        sa.Column('cwe_id', sa.String(length=20), nullable=True),
        sa.Column('created_at', sa.DateTime(), nullable=False),
        sa.Column('updated_at', sa.DateTime(), nullable=False),
        sa.ForeignKeyConstraint(['asset_id'], ['assets.id'], ),
        sa.PrimaryKeyConstraint('id')
    )
    op.create_index('ix_vulnerabilities_id', 'vulnerabilities', ['id'])
    op.create_index('ix_vulnerabilities_cve_id', 'vulnerabilities', ['cve_id'])
    op.create_index('ix_vulnerabilities_asset_id', 'vulnerabilities', ['asset_id'])
    op.create_index('ix_vulnerabilities_cvss_score', 'vulnerabilities', ['cvss_score'])
    op.create_index('ix_vulnerabilities_severity', 'vulnerabilities', ['severity'])
    op.create_index('ix_vulnerabilities_status', 'vulnerabilities', ['status'])
    op.create_index('ix_vulnerabilities_package_name', 'vulnerabilities', ['package_name'])
    op.create_index('ix_vulnerabilities_exploitable', 'vulnerabilities', ['exploitable'])
    op.create_index('ix_vulnerabilities_detected_at', 'vulnerabilities', ['detected_at'])
    op.create_index('idx_vuln_status_severity', 'vulnerabilities', ['status', 'severity'])
    op.create_index('idx_vuln_asset_status', 'vulnerabilities', ['asset_id', 'status'])

    # Scans Tabelle
    op.create_table(
        'scans',
        sa.Column('id', sa.Integer(), nullable=False),
        sa.Column('asset_id', sa.Integer(), nullable=False),
        sa.Column('scan_type', sa.Enum('FULL', 'SYSCOLLECTOR', 'MANUAL', name='scantype'), nullable=False),
        sa.Column('status', sa.Enum('PENDING', 'RUNNING', 'COMPLETED', 'FAILED', name='scanstatus'), nullable=False),
        sa.Column('started_at', sa.DateTime(), nullable=True),
        sa.Column('completed_at', sa.DateTime(), nullable=True),
        sa.Column('vulnerabilities_found', sa.Integer(), nullable=False),
        sa.Column('vulnerabilities_new', sa.Integer(), nullable=False),
        sa.Column('vulnerabilities_resolved', sa.Integer(), nullable=False),
        sa.Column('error_message', sa.Text(), nullable=True),
        sa.Column('created_at', sa.DateTime(), nullable=False),
        sa.Column('updated_at', sa.DateTime(), nullable=False),
        sa.ForeignKeyConstraint(['asset_id'], ['assets.id'], ),
        sa.PrimaryKeyConstraint('id')
    )
    op.create_index('ix_scans_id', 'scans', ['id'])
    op.create_index('ix_scans_asset_id', 'scans', ['asset_id'])
    op.create_index('ix_scans_status', 'scans', ['status'])

    # AI Analyses Tabelle
    op.create_table(
        'ai_analyses',
        sa.Column('id', sa.Integer(), nullable=False),
        sa.Column('vulnerability_id', sa.Integer(), nullable=False),
        sa.Column('analysis_text', sa.Text(), nullable=False),
        sa.Column('threat_level', sa.String(length=20), nullable=True),
        sa.Column('exploits_found', sa.Text(), nullable=True),
        sa.Column('workarounds', sa.Text(), nullable=True),
        sa.Column('remediation_steps', sa.Text(), nullable=True),
        sa.Column('threat_intel_sources', sa.Text(), nullable=True),
        sa.Column('model_version', sa.String(length=50), nullable=True),
        sa.Column('confidence_score', sa.String(length=20), nullable=True),
        sa.Column('analysis_timestamp', sa.DateTime(), nullable=False),
        sa.Column('cache_expires_at', sa.DateTime(), nullable=False),
        sa.Column('created_at', sa.DateTime(), nullable=False),
        sa.Column('updated_at', sa.DateTime(), nullable=False),
        sa.ForeignKeyConstraint(['vulnerability_id'], ['vulnerabilities.id'], ),
        sa.PrimaryKeyConstraint('id')
    )
    op.create_index('ix_ai_analyses_id', 'ai_analyses', ['id'])
    op.create_index('ix_ai_analyses_vulnerability_id', 'ai_analyses', ['vulnerability_id'], unique=True)

    # Audit Logs Tabelle
    op.create_table(
        'audit_logs',
        sa.Column('id', sa.Integer(), nullable=False),
        sa.Column('user_id', sa.Integer(), nullable=True),
        sa.Column('event_type', sa.Enum('LOGIN_SUCCESS', 'LOGIN_FAILED', 'LOGOUT', 'PASSWORD_CHANGE', 'ACCESS_DENIED', 'PERMISSION_ESCALATION', 'VULNERABILITY_UPDATED', 'ASSET_CREATED', 'ASSET_UPDATED', 'ASSET_DELETED', 'SCAN_TRIGGERED', 'SCAN_COMPLETED', 'AI_ANALYSIS_REQUESTED', 'CONFIG_CHANGE', 'SECURITY_ALERT', name='auditeventtype'), nullable=False),
        sa.Column('event_description', sa.String(length=500), nullable=False),
        sa.Column('ip_address', sa.String(length=45), nullable=True),
        sa.Column('user_agent', sa.String(length=500), nullable=True),
        sa.Column('resource_type', sa.String(length=50), nullable=True),
        sa.Column('resource_id', sa.String(length=50), nullable=True),
        sa.Column('old_value', sa.Text(), nullable=True),
        sa.Column('new_value', sa.Text(), nullable=True),
        sa.Column('timestamp', sa.DateTime(), nullable=False),
        sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
        sa.PrimaryKeyConstraint('id')
    )
    op.create_index('ix_audit_logs_id', 'audit_logs', ['id'])
    op.create_index('ix_audit_logs_user_id', 'audit_logs', ['user_id'])
    op.create_index('ix_audit_logs_event_type', 'audit_logs', ['event_type'])
    op.create_index('ix_audit_logs_timestamp', 'audit_logs', ['timestamp'])


def downgrade() -> None:
    # Lösche Tabellen in umgekehrter Reihenfolge (wegen Foreign Keys)
    op.drop_table('settings')
    op.drop_table('audit_logs')
    op.drop_table('ai_analyses')
    op.drop_table('scans')
    op.drop_table('vulnerabilities')
    op.drop_table('assets')
    op.drop_table('users')

    # Lösche ENUM-Typen
    op.execute("DROP TYPE auditeventtype")
    op.execute("DROP TYPE scanstatus")
    op.execute("DROP TYPE scantype")
    op.execute("DROP TYPE vulnerabilitystatus")
    op.execute("DROP TYPE vulnerabilityseverity")
    op.execute("DROP TYPE assetstatus")
    op.execute("DROP TYPE assetsource")
    op.execute("DROP TYPE userrole")
