using System;
using System.Collections.Generic;
using System.Data;
using System.Threading.Tasks;
using Xunit;
using Npgsql;
using Dapper;
using QuantEngine.Core.Interfaces;
using QuantEngine.Infrastructure.Repositories;
using QuantEngine.Application.Services;
using QuantEngine.Infrastructure.Data;

namespace QuantEngine.Core.Tests
{
    public class HistoryIngestionPgTests
    {
        private sealed class DirectDbConnectionFactory : IDbConnectionFactory
        {
            private readonly string _connectionString;

            public DirectDbConnectionFactory(string connectionString)
            {
                _connectionString = connectionString;
            }

            public IDbConnection CreateConnection()
            {
                return new NpgsqlConnection(_connectionString);
            }
        }

        [Fact]
        public async Task AppendFiveFactorOutputsToRealPostgresDb()
        {
            // List of candidate connection strings to try
            var candidates = new List<string>();

            string? dsnEnv = Environment.GetEnvironmentVariable("QE_WBS_PG_DSN");
            if (!string.IsNullOrWhiteSpace(dsnEnv)) candidates.Add(dsnEnv);

            string? connStrEnv = Environment.GetEnvironmentVariable("ConnectionStrings__DefaultConnection");
            if (!string.IsNullOrWhiteSpace(connStrEnv)) candidates.Add(connStrEnv);

            // Add standard local docker port fallbacks (both 15432 and 5432)
            candidates.Add("Host=127.0.0.1;Port=15432;Database=quantenginedb;Username=postgres;Password=postgres;Search Path=quantengine");
            candidates.Add("Host=127.0.0.1;Port=5432;Database=quantenginedb;Username=postgres;Password=postgres;Search Path=quantengine");
            candidates.Add("Host=127.0.0.1;Port=15432;Database=quantenginedb;Username=quantengine_ci;Password=quantengine_ci;Search Path=quantengine");
            candidates.Add("Host=127.0.0.1;Port=5432;Database=quantenginedb;Username=quantengine_ci;Password=quantengine_ci;Search Path=quantengine");

            string? successfulConnStr = null;
            foreach (var rawDsn in candidates)
            {
                string connStr = rawDsn;
                if (rawDsn.Contains("host=") || rawDsn.Contains("dbname="))
                {
                    try
                    {
                        var builder = new NpgsqlConnectionStringBuilder();
                        var parts = rawDsn.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                        foreach (var p in parts)
                        {
                            var kv = p.Split('=', 2);
                            if (kv.Length == 2)
                            {
                                string key = kv[0].Trim().ToLowerInvariant();
                                string val = kv[1].Trim().Trim('\'').Trim('"');
                                if (key == "host") builder.Host = val;
                                else if (key == "port" && int.TryParse(val, out var pi)) builder.Port = pi;
                                else if (key == "dbname") builder.Database = val;
                                else if (key == "user") builder.Username = val;
                                else if (key == "password") builder.Password = val;
                            }
                        }
                        builder.SearchPath = "quantengine";
                        connStr = builder.ConnectionString;
                    }
                    catch
                    {
                        // Fallback to rawDsn if builder logic fails
                        connStr = rawDsn;
                    }
                }

                try
                {
                    using var conn = new NpgsqlConnection(connStr);
                    await conn.OpenAsync();
                    successfulConnStr = connStr;
                    break; // Found working connection
                }
                catch
                {
                    // Continue to next candidate
                }
            }

            if (successfulConnStr == null)
            {
                Console.WriteLine("No valid active PostgreSQL connection found. Ingestion skipped.");
                return;
            }

            Console.WriteLine($"Selected connection: {successfulConnStr}");
            
            // Explicitly create database schema and tables to guarantee existence in tests
            try
            {
                using var conn = new NpgsqlConnection(successfulConnStr);
                await conn.OpenAsync();
                
                // Create engine_history schema and table
                await conn.ExecuteAsync("CREATE SCHEMA IF NOT EXISTS engine_history;");
                await conn.ExecuteAsync(@"
                    CREATE TABLE IF NOT EXISTS engine_history.factor_output_history (
                        id BIGSERIAL PRIMARY KEY,
                        factor_output_id TEXT NOT NULL,
                        observed_at TEXT NOT NULL,
                        factor_id TEXT NOT NULL,
                        factor_version TEXT NOT NULL,
                        output_value TEXT NOT NULL,
                        output_gate TEXT NOT NULL,
                        source_version TEXT NOT NULL,
                        provenance JSONB NOT NULL DEFAULT '{}'::jsonb,
                        created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
                    );
                ");

                // Create quantengine schema and account tables
                await conn.ExecuteAsync("CREATE SCHEMA IF NOT EXISTS quantengine;");
                await conn.ExecuteAsync(@"
                    CREATE TABLE IF NOT EXISTS quantengine.workspace_account (
                        ordinal INT NOT NULL,
                        username TEXT PRIMARY KEY,
                        password_hash TEXT NOT NULL,
                        role TEXT NOT NULL DEFAULT 'Admin',
                        is_active TEXT NOT NULL DEFAULT 'true',
                        created_at TEXT NOT NULL,
                        updated_at TEXT NOT NULL
                    );
                ");

                // Insert admin user
                await conn.ExecuteAsync(@"
                    INSERT INTO quantengine.workspace_account (
                        ordinal,
                        username,
                        password_hash,
                        role,
                        is_active,
                        created_at,
                        updated_at
                    )
                    VALUES (
                        1,
                        'admin',
                        '4CA7545981323E02F374EC9CB283D658E66F0682822801E4953435188CDCD410',
                        'Admin',
                        'true',
                        NOW()::text,
                        NOW()::text
                    )
                    ON CONFLICT (username) DO UPDATE
                    SET password_hash = EXCLUDED.password_hash;
                ");

                Console.WriteLine("Explicit schema generation and admin seeding complete.");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Explicit Schema Generation failed: {ex.Message}");
            }

            var factory = new DirectDbConnectionFactory(successfulConnStr);
            var store = new PostgresqlHistoryStore(factory);
            var ingestion = new HistoryIngestionService(store);

            int successCount = 0;
            for (int i = 1; i <= 5; i++)
            {
                int res = await ingestion.AppendFactorOutputAsync(
                    factorId: $"SS001_COMPOSITE_FACTOR_{i}",
                    factorVersion: "v1.0.0",
                    outputValue: 42.0 + i,
                    outputGate: "PASS",
                    sourceVersion: "QE-M3-03-TEST",
                    observedAt: DateTimeOffset.UtcNow
                );
                successCount += res;
            }

            Assert.Equal(5, successCount);
            Console.WriteLine("Successfully ingested 5 factor records into database.");
        }
    }
}
