using Dapper;
// Asegúrate de que BCrypt.Net esté instalado en este proyecto o referenciado
// Si no te reconoce BCrypt, avísame, pero debería funcionar porque ya se usa en AuthService.

namespace SIGCM.Infrastructure.Data;

public class DbInitializer
{
    private readonly IDbConnectionFactory _connectionFactory;

    public DbInitializer(IDbConnectionFactory connectionFactory)
    {
        _connectionFactory = connectionFactory;
    }

    public async Task InitializeAsync()
    {
        using var connection = _connectionFactory.CreateConnection();

        var schemaSql = @"
IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'Users')
BEGIN
    CREATE TABLE Users (
        Id INT IDENTITY(1,1) PRIMARY KEY,
        Username NVARCHAR(50) NOT NULL UNIQUE,
        PasswordHash NVARCHAR(255) NOT NULL,
        Role NVARCHAR(20) NOT NULL,
        Email NVARCHAR(100) NULL,
        CreatedAt DATETIME DEFAULT GETUTCDATE()
    );
END

IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'Categories')
BEGIN
    CREATE TABLE Categories (
        Id INT IDENTITY(1,1) PRIMARY KEY,
        ParentId INT NULL,
        Name NVARCHAR(100) NOT NULL,
        Slug NVARCHAR(100) NOT NULL,
        Active BIT DEFAULT 1,
        FOREIGN KEY (ParentId) REFERENCES Categories(Id)
    );
END

IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'Operations')
BEGIN
    CREATE TABLE Operations (
        Id INT IDENTITY(1,1) PRIMARY KEY,
        Name NVARCHAR(50) NOT NULL UNIQUE
    );
END

IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'CategoryOperations')
BEGIN
    CREATE TABLE CategoryOperations (
        CategoryId INT NOT NULL,
        OperationId INT NOT NULL,
        PRIMARY KEY (CategoryId, OperationId),
        FOREIGN KEY (CategoryId) REFERENCES Categories(Id) ON DELETE CASCADE,
        FOREIGN KEY (OperationId) REFERENCES Operations(Id) ON DELETE CASCADE
    );
END

IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'AttributeDefinitions')
BEGIN
    CREATE TABLE AttributeDefinitions (
        Id INT IDENTITY(1,1) PRIMARY KEY,
        CategoryId INT NOT NULL,
        Name NVARCHAR(100) NOT NULL,
        DataType NVARCHAR(20) NOT NULL,
        Required BIT DEFAULT 0,
        FOREIGN KEY (CategoryId) REFERENCES Categories(Id) ON DELETE CASCADE
    );
END

IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'Listings')
BEGIN
    CREATE TABLE Listings (
        Id INT IDENTITY(1,1) PRIMARY KEY,
        CategoryId INT NOT NULL,
        OperationId INT NOT NULL,
        Title NVARCHAR(200) NOT NULL,
        Description NVARCHAR(MAX) NULL,
        Price DECIMAL(18,2) NOT NULL DEFAULT 0,
        Currency NVARCHAR(3) DEFAULT 'ARS',
        CreatedAt DATETIME2 DEFAULT GETUTCDATE(),
        Status NVARCHAR(20) DEFAULT 'Draft',
        UserId INT NULL,
        FOREIGN KEY (CategoryId) REFERENCES Categories(Id),
        FOREIGN KEY (OperationId) REFERENCES Operations(Id),
        FOREIGN KEY (UserId) REFERENCES Users(Id)
    );
END

IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'ListingAttributeValues')
BEGIN
    CREATE TABLE ListingAttributeValues (
        Id INT IDENTITY(1,1) PRIMARY KEY,
        ListingId INT NOT NULL,
        AttributeDefinitionId INT NOT NULL,
        Value NVARCHAR(MAX) NOT NULL,
        FOREIGN KEY (ListingId) REFERENCES Listings(Id) ON DELETE CASCADE,
        FOREIGN KEY (AttributeDefinitionId) REFERENCES AttributeDefinitions(Id) ON DELETE NO ACTION
    );
END

IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'ListingImages')
BEGIN
    CREATE TABLE ListingImages (
        Id INT IDENTITY(1,1) PRIMARY KEY,
        ListingId INT NOT NULL,
        Url NVARCHAR(500) NOT NULL,
        IsMainInfo BIT DEFAULT 0,
        DisplayOrder INT DEFAULT 0,
        FOREIGN KEY (ListingId) REFERENCES Listings(Id) ON DELETE CASCADE
    );
END
";
        // Ejecutar creación de tablas base
        await connection.ExecuteAsync(schemaSql);

        // --- MIGRACIONES (Schema Update) ---
        var migrationSql = @"
            -- Listings Columns
            IF NOT EXISTS(SELECT * FROM sys.columns WHERE Name = N'PublicationStartDate' AND Object_ID = Object_ID(N'Listings'))
                ALTER TABLE Listings ADD PublicationStartDate DATETIME2 NULL;
            IF NOT EXISTS(SELECT * FROM sys.columns WHERE Name = N'ApprovedAt' AND Object_ID = Object_ID(N'Listings'))
                ALTER TABLE Listings ADD ApprovedAt DATETIME2 NULL;
            IF NOT EXISTS(SELECT * FROM sys.columns WHERE Name = N'IsFeatured' AND Object_ID = Object_ID(N'Listings'))
                ALTER TABLE Listings ADD IsFeatured BIT NOT NULL DEFAULT 0;
            IF NOT EXISTS(SELECT * FROM sys.columns WHERE Name = N'FeaturedExpiry' AND Object_ID = Object_ID(N'Listings'))
                ALTER TABLE Listings ADD FeaturedExpiry DATETIME2 NULL;
            IF NOT EXISTS(SELECT * FROM sys.columns WHERE Name = N'AllowContact' AND Object_ID = Object_ID(N'Listings'))
                ALTER TABLE Listings ADD AllowContact BIT NOT NULL DEFAULT 1;

            -- Users Columns (Security & Profiles)
            IF NOT EXISTS(SELECT * FROM sys.columns WHERE Name = N'BillingName' AND Object_ID = Object_ID(N'Users'))
                ALTER TABLE Users ADD BillingName NVARCHAR(200) NULL;
            IF NOT EXISTS(SELECT * FROM sys.columns WHERE Name = N'BillingAddress' AND Object_ID = Object_ID(N'Users'))
                ALTER TABLE Users ADD BillingAddress NVARCHAR(500) NULL;
            IF NOT EXISTS(SELECT * FROM sys.columns WHERE Name = N'BillingTaxId' AND Object_ID = Object_ID(N'Users'))
                ALTER TABLE Users ADD BillingTaxId NVARCHAR(50) NULL;
            IF NOT EXISTS(SELECT * FROM sys.columns WHERE Name = N'BillingTaxType' AND Object_ID = Object_ID(N'Users'))
                ALTER TABLE Users ADD BillingTaxType NVARCHAR(50) NULL;
            IF NOT EXISTS(SELECT * FROM sys.columns WHERE Name = N'ClientId' AND Object_ID = Object_ID(N'Users'))
                ALTER TABLE Users ADD ClientId INT NULL;

            -- Audit & Security Columns for Users
            IF NOT EXISTS(SELECT * FROM sys.columns WHERE Name = N'FailedLoginAttempts' AND Object_ID = Object_ID(N'Users'))
                ALTER TABLE Users ADD FailedLoginAttempts INT NOT NULL DEFAULT 0;
            IF NOT EXISTS(SELECT * FROM sys.columns WHERE Name = N'LockoutEnd' AND Object_ID = Object_ID(N'Users'))
                ALTER TABLE Users ADD LockoutEnd DATETIME NULL;
            IF NOT EXISTS(SELECT * FROM sys.columns WHERE Name = N'MustChangePassword' AND Object_ID = Object_ID(N'Users'))
                ALTER TABLE Users ADD MustChangePassword BIT NOT NULL DEFAULT 1;
            IF NOT EXISTS(SELECT * FROM sys.columns WHERE Name = N'IsActive' AND Object_ID = Object_ID(N'Users'))
                ALTER TABLE Users ADD IsActive BIT NOT NULL DEFAULT 1;
            IF NOT EXISTS(SELECT * FROM sys.columns WHERE Name = N'LastLogin' AND Object_ID = Object_ID(N'Users'))
                ALTER TABLE Users ADD LastLogin DATETIME NULL;
            IF NOT EXISTS(SELECT * FROM sys.columns WHERE Name = N'GoogleId' AND Object_ID = Object_ID(N'Users'))
                ALTER TABLE Users ADD GoogleId NVARCHAR(255) NULL;
            IF NOT EXISTS(SELECT * FROM sys.columns WHERE Name = N'IsMfaEnabled' AND Object_ID = Object_ID(N'Users'))
                ALTER TABLE Users ADD IsMfaEnabled BIT NOT NULL DEFAULT 0;
            IF NOT EXISTS(SELECT * FROM sys.columns WHERE Name = N'MfaSecret' AND Object_ID = Object_ID(N'Users'))
                ALTER TABLE Users ADD MfaSecret NVARCHAR(255) NULL;
            IF NOT EXISTS(SELECT * FROM sys.columns WHERE Name = N'Phone' AND Object_ID = Object_ID(N'Users'))
                ALTER TABLE Users ADD Phone NVARCHAR(50) NULL;

            -- Coupons Table
            IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'Coupons')
            BEGIN
                CREATE TABLE Coupons (
                    Id INT IDENTITY(1,1) PRIMARY KEY,
                    Code NVARCHAR(50) NOT NULL,
                    DiscountType NVARCHAR(20) NOT NULL,
                    DiscountValue DECIMAL(18,2) NOT NULL,
                    ExpiryDate DATETIME2 NULL,
                    UsageCount INT NOT NULL DEFAULT 0,
                    MaxUsages INT NULL,
                    IsActive BIT NOT NULL DEFAULT 1,
                    CreatedAt DATETIME2 NOT NULL DEFAULT GETUTCDATE()
                );
                CREATE UNIQUE INDEX IX_Coupons_Code ON Coupons(Code);
                
                -- Seed Default Coupon
                INSERT INTO Coupons (Code, DiscountType, DiscountValue) VALUES ('WELCOME2025', 'Percentage', 10.00);
            END

            -- Listings CouponCode
            IF NOT EXISTS(SELECT * FROM sys.columns WHERE Name = N'CouponCode' AND Object_ID = Object_ID(N'Listings'))
                ALTER TABLE Listings ADD CouponCode NVARCHAR(50) NULL;

            -- Coupons MaxUsagesPerUser
            IF NOT EXISTS(SELECT * FROM sys.columns WHERE Name = N'MaxUsagesPerUser' AND Object_ID = Object_ID(N'Coupons'))
                ALTER TABLE Coupons ADD MaxUsagesPerUser INT NULL;

            -- Tabla de Notas/Mensajes de Avisos
            IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'ListingNotes') AND type in (N'U'))
            BEGIN
                CREATE TABLE ListingNotes (
                    Id INT IDENTITY(1,1) PRIMARY KEY,
                    ListingId INT NOT NULL,
                    SenderId INT NOT NULL,
                    IsFromModerator BIT NOT NULL DEFAULT 1,
                    Message NVARCHAR(MAX) NOT NULL,
                    CreatedAt DATETIME2 NOT NULL DEFAULT GETUTCDATE(),
                    IsRead BIT NOT NULL DEFAULT 0,
                    FOREIGN KEY (ListingId) REFERENCES Listings(Id) ON DELETE CASCADE,
                    FOREIGN KEY (SenderId) REFERENCES Users(Id)
                );
            END
            ELSE
            BEGIN
                -- Migración gradual si la tabla ya existía con ModeratorId
                IF EXISTS(SELECT * FROM sys.columns WHERE Name = N'ModeratorId' AND Object_ID = Object_ID(N'ListingNotes'))
                BEGIN
                    EXEC sp_rename 'ListingNotes.ModeratorId', 'SenderId', 'COLUMN';
                END
                
                IF NOT EXISTS(SELECT * FROM sys.columns WHERE Name = N'IsFromModerator' AND Object_ID = Object_ID(N'ListingNotes'))
                BEGIN
                    ALTER TABLE ListingNotes ADD IsFromModerator BIT NOT NULL DEFAULT 1;
                END
            END
        ";
        await connection.ExecuteAsync(migrationSql);

        // --- MIGRACIÓN DE DATOS (Post-Schema Update) ---
        var dataMigrationSql = @"
            -- Data Migration: Clients to Users
            IF EXISTS (SELECT * FROM sys.tables WHERE name = 'Clients')
            BEGIN
                -- Verificamos si la tabla Clients tiene la columna Phone para evitar errores
                DECLARE @phoneColumnExists BIT = 0;
                IF EXISTS(SELECT * FROM sys.columns WHERE Name = N'Phone' AND Object_ID = Object_ID(N'Clients'))
                    SET @phoneColumnExists = 1;

                IF @phoneColumnExists = 1
                BEGIN
                    INSERT INTO Users (Username, PasswordHash, Role, Email, BillingName, BillingTaxId, BillingAddress, Phone, MustChangePassword, IsActive)
                    SELECT 
                        ISNULL(DniOrCuit, Name) as Username,
                        'N/A' as PasswordHash,
                        'Client' as Role,
                        Email,
                        Name as BillingName,
                        DniOrCuit as BillingTaxId,
                        Address as BillingAddress,
                        Phone,
                        0 as MustChangePassword,
                        1 as IsActive
                    FROM Clients
                    WHERE DniOrCuit NOT IN (SELECT BillingTaxId FROM Users WHERE BillingTaxId IS NOT NULL);
                END
                ELSE
                BEGIN
                    INSERT INTO Users (Username, PasswordHash, Role, Email, BillingName, BillingTaxId, BillingAddress, MustChangePassword, IsActive)
                    SELECT 
                        ISNULL(DniOrCuit, Name) as Username,
                        'N/A' as PasswordHash,
                        'Client' as Role,
                        Email,
                        Name as BillingName,
                        DniOrCuit as BillingTaxId,
                        Address as BillingAddress,
                        0 as MustChangePassword,
                        1 as IsActive
                    FROM Clients
                    WHERE DniOrCuit NOT IN (SELECT BillingTaxId FROM Users WHERE BillingTaxId IS NOT NULL);
                END

                -- 1. Actualizar Listings para que apunten a Users antes de romper nada (opcional pero recomendado si hay datos)
                -- Solo lo hacemos para los que ya existen en Users por BillingTaxId
                UPDATE l
                SET l.ClientId = u.Id
                FROM Listings l
                JOIN Clients c ON l.ClientId = c.Id
                JOIN Users u ON c.DniOrCuit = u.BillingTaxId;

                -- 2. Eliminar todas las llaves foráneas que apuntan a Clients
                DECLARE @sql NVARCHAR(MAX) = N'';
                SELECT @sql += 'ALTER TABLE ' + QUOTENAME(OBJECT_SCHEMA_NAME(parent_object_id)) + '.' + QUOTENAME(OBJECT_NAME(parent_object_id)) + 
                               ' DROP CONSTRAINT ' + QUOTENAME(name) + ';'
                FROM sys.foreign_keys
                WHERE referenced_object_id = OBJECT_ID('Clients');
                
                IF @sql <> '' EXEC sp_executesql @sql;

                -- 3. Finalmente borrar la tabla
                DROP TABLE Clients;
            END
        ";
        await connection.ExecuteAsync(dataMigrationSql);

        // --- SEED DE DATOS (Usuario Admin) ---
        var adminCount = await connection.ExecuteScalarAsync<int>("SELECT COUNT(1) FROM Users WHERE Username = 'admin'");

        if (adminCount == 0)
        {
            // Creamos el hash válido para la clave del usuario usando la librería del proyecto
            var passwordHash = BCrypt.Net.BCrypt.HashPassword("Diagonal423");

            var insertAdminSql = @"
                INSERT INTO Users (Username, PasswordHash, Role, Email)
                VALUES ('admin', @PasswordHash, 'Admin', 'admin@sigcm.com')";

            await connection.ExecuteAsync(insertAdminSql, new { PasswordHash = passwordHash });
        }
    }
}