using System;
using System.Collections.Generic;
using Npgsql;
using WingsAPI.Data.Drops;
using WingsAPI.Data.Shops;
using WingsEmu.DTOs.Maps;
using WingsEmu.DTOs.Recipes;
using WingsEmu.DTOs.ServerDatas;
using WingsEmu.DTOs.Shops;
using WingsEmu.Packets.Enums;

namespace WingsEmu.Plugins.BasicImplementations.ServerConfigs.Persistence;

public static class ParserDataPostgresReader
{
    public static bool DbFirstEnabled => string.Equals(Environment.GetEnvironmentVariable("DB_FIRST"), "true", StringComparison.OrdinalIgnoreCase);

    public static bool StrictDbOnlyEnabled => string.Equals(Environment.GetEnvironmentVariable("STRICT_DB_ONLY"), "true", StringComparison.OrdinalIgnoreCase);

    private static string BuildConnectionString()
    {
        string host = Environment.GetEnvironmentVariable("DATABASE_IP")
                      ?? Environment.GetEnvironmentVariable("POSTGRES_DATABASE_IP")
                      ?? "127.0.0.1";
        string port = Environment.GetEnvironmentVariable("DATABASE_PORT")
                      ?? Environment.GetEnvironmentVariable("POSTGRES_DATABASE_PORT")
                      ?? "5432";
        string db = Environment.GetEnvironmentVariable("DATABASE_NAME")
                    ?? Environment.GetEnvironmentVariable("POSTGRES_DATABASE_NAME")
                    ?? "game";
        string user = Environment.GetEnvironmentVariable("DATABASE_USER")
                      ?? Environment.GetEnvironmentVariable("POSTGRES_DATABASE_USER")
                      ?? "postgres";
        string pass = Environment.GetEnvironmentVariable("DATABASE_PASSWORD")
                      ?? Environment.GetEnvironmentVariable("POSTGRES_DATABASE_PASSWORD")
                      ?? "postgres";

        return $"Host={host};Port={port};Database={db};Username={user};Password={pass}";
    }

    public static List<DropDTO> LoadDrops()
    {
        var result = new List<DropDTO>();
        using var conn = new NpgsqlConnection(BuildConnectionString());
        conn.Open();
        using var cmd = new NpgsqlCommand("SELECT drop_id, amount, drop_chance, item_vnum, map_id, monster_vnum, race_type, race_sub_type FROM drops;", conn);
        using var reader = cmd.ExecuteReader();
        while (reader.Read())
        {
            result.Add(new DropDTO
            {
                Id = reader.GetInt32(0),
                Amount = reader.GetInt32(1),
                DropChance = reader.GetInt32(2),
                ItemVNum = reader.GetInt32(3),
                MapId = reader.IsDBNull(4) ? null : reader.GetInt32(4),
                MonsterVNum = reader.IsDBNull(5) ? null : reader.GetInt32(5),
                RaceType = reader.IsDBNull(6) ? null : reader.GetInt32(6),
                RaceSubType = reader.IsDBNull(7) ? null : reader.GetInt32(7)
            });
        }

        return result;
    }

    public static List<MapMonsterDTO> LoadMapMonsters()
    {
        var result = new List<MapMonsterDTO>();
        using var conn = new NpgsqlConnection(BuildConnectionString());
        conn.Open();
        using var cmd = new NpgsqlCommand("SELECT map_monster_id, map_id, vnum, map_x, map_y, direction, can_move FROM map_monsters;", conn);
        using var reader = cmd.ExecuteReader();
        while (reader.Read())
        {
            result.Add(new MapMonsterDTO
            {
                Id = reader.GetInt32(0),
                MapId = reader.GetInt32(1),
                MonsterVNum = reader.GetInt32(2),
                MapX = reader.GetInt16(3),
                MapY = reader.GetInt16(4),
                Direction = reader.IsDBNull(5) ? (byte)0 : reader.GetByte(5),
                IsMoving = !reader.IsDBNull(6) && reader.GetBoolean(6)
            });
        }

        return result;
    }

    public static List<MapNpcDTO> LoadMapNpcs()
    {
        var result = new List<MapNpcDTO>();
        using var conn = new NpgsqlConnection(BuildConnectionString());
        conn.Open();
        using var cmd = new NpgsqlCommand("SELECT map_npc_id, map_id, vnum, pos_x, pos_y, effect_vnum, effect_delay, dialog_id, direction_facing FROM map_npcs;", conn);
        using var reader = cmd.ExecuteReader();
        while (reader.Read())
        {
            result.Add(new MapNpcDTO
            {
                Id = reader.GetInt32(0),
                MapId = reader.GetInt32(1),
                NpcVNum = reader.GetInt32(2),
                MapX = reader.GetInt16(3),
                MapY = reader.GetInt16(4),
                Effect = reader.IsDBNull(5) ? (short)0 : reader.GetInt16(5),
                EffectDelay = reader.IsDBNull(6) ? (short)0 : reader.GetInt16(6),
                Dialog = reader.IsDBNull(7) ? (short)0 : reader.GetInt16(7),
                Direction = reader.IsDBNull(8) ? (byte)0 : reader.GetByte(8)
            });
        }

        return result;
    }

    public static List<ShopDTO> LoadShops()
    {
        var shops = new Dictionary<int, ShopDTO>();
        using var conn = new NpgsqlConnection(BuildConnectionString());
        conn.Open();

        using (var cmd = new NpgsqlCommand("SELECT map_npc_id, menu_type, shop_type, name FROM shops;", conn))
        using (var reader = cmd.ExecuteReader())
        {
            while (reader.Read())
            {
                int mapNpcId = reader.GetInt32(0);
                shops[mapNpcId] = new ShopDTO
                {
                    MapNpcId = mapNpcId,
                    MenuType = (byte)reader.GetInt32(1),
                    ShopType = (byte)reader.GetInt32(2),
                    Name = reader.IsDBNull(3) ? null : reader.GetString(3),
                    Items = new List<ShopItemDTO>(),
                    Skills = new List<ShopSkillDTO>()
                };
            }
        }

        using (var cmd = new NpgsqlCommand("SELECT map_npc_id, slot, color, item_vnum, rare, type, upgrade, price FROM shop_items;", conn))
        using (var reader = cmd.ExecuteReader())
        {
            while (reader.Read())
            {
                int mapNpcId = reader.GetInt32(0);
                if (!shops.TryGetValue(mapNpcId, out ShopDTO shop))
                {
                    continue;
                }

                shop.Items ??= new List<ShopItemDTO>();
                shop.Items.Add(new ShopItemDTO
                {
                    Slot = reader.GetInt16(1),
                    Color = reader.GetByte(2),
                    ItemVNum = reader.GetInt32(3),
                    Rare = reader.GetInt16(4),
                    Type = reader.GetByte(5),
                    Upgrade = reader.GetByte(6),
                    Price = reader.IsDBNull(7) ? null : reader.GetInt32(7)
                });
            }
        }

        using (var cmd = new NpgsqlCommand("SELECT map_npc_id, skill_vnum, slot, type FROM shop_skills;", conn))
        using (var reader = cmd.ExecuteReader())
        {
            while (reader.Read())
            {
                int mapNpcId = reader.GetInt32(0);
                if (!shops.TryGetValue(mapNpcId, out ShopDTO shop))
                {
                    continue;
                }

                shop.Skills ??= new List<ShopSkillDTO>();
                shop.Skills.Add(new ShopSkillDTO
                {
                    SkillVNum = reader.GetInt16(1),
                    Slot = reader.GetInt16(2),
                    Type = reader.GetByte(3)
                });
            }
        }

        return new List<ShopDTO>(shops.Values);
    }

    public static List<RecipeDTO> LoadRecipes()
    {
        var recipes = new Dictionary<int, RecipeDTO>();
        using var conn = new NpgsqlConnection(BuildConnectionString());
        conn.Open();

        using (var cmd = new NpgsqlCommand("SELECT recipe_id, amount, producer_map_npc_id, produced_item_vnum, producer_item_vnum, producer_npc_vnum FROM recipes;", conn))
        using (var reader = cmd.ExecuteReader())
        {
            while (reader.Read())
            {
                int recipeId = reader.GetInt32(0);
                recipes[recipeId] = new RecipeDTO
                {
                    Id = recipeId,
                    Amount = reader.GetInt32(1),
                    ProducerMapNpcId = reader.IsDBNull(2) ? null : reader.GetInt32(2),
                    ProducedItemVnum = reader.GetInt32(3),
                    ProducerItemVnum = reader.IsDBNull(4) ? null : reader.GetInt32(4),
                    ProducerNpcVnum = reader.IsDBNull(5) ? null : reader.GetInt32(5),
                    Items = new List<RecipeItemDTO>()
                };
            }
        }

        using (var cmd = new NpgsqlCommand("SELECT recipe_id, slot, item_vnum, amount FROM recipe_items;", conn))
        using (var reader = cmd.ExecuteReader())
        {
            while (reader.Read())
            {
                int recipeId = reader.GetInt32(0);
                if (!recipes.TryGetValue(recipeId, out RecipeDTO recipe))
                {
                    continue;
                }

                recipe.Items ??= new List<RecipeItemDTO>();
                recipe.Items.Add(new RecipeItemDTO
                {
                    Slot = reader.GetInt16(1),
                    ItemVNum = reader.GetInt16(2),
                    Amount = reader.GetInt16(3)
                });
            }
        }

        return new List<RecipeDTO>(recipes.Values);
    }

    public static List<TeleporterDTO> LoadTeleporters()
    {
        var result = new List<TeleporterDTO>();
        using var conn = new NpgsqlConnection(BuildConnectionString());
        conn.Open();
        using var cmd = new NpgsqlCommand("SELECT teleporter_id, idx, type, map_id, map_npc_id, map_x, map_y FROM map_teleporters;", conn);
        using var reader = cmd.ExecuteReader();
        while (reader.Read())
        {
            result.Add(new TeleporterDTO
            {
                Id = reader.GetInt32(0),
                Index = reader.GetInt16(1),
                Type = (TeleporterType)reader.GetInt32(2),
                MapId = reader.GetInt32(3),
                MapNpcId = reader.GetInt32(4),
                MapX = reader.GetInt16(5),
                MapY = reader.GetInt16(6)
            });
        }

        return result;
    }

    public static List<ItemBoxDto> LoadItemBoxes()
    {
        var boxes = new Dictionary<int, ItemBoxDto>();
        using var conn = new NpgsqlConnection(BuildConnectionString());
        conn.Open();

        using (var cmd = new NpgsqlCommand("SELECT item_vnum, box_type, min_rewards, max_rewards, shows_raid_panel FROM item_boxes;", conn))
        using (var reader = cmd.ExecuteReader())
        {
            while (reader.Read())
            {
                int itemVnum = reader.GetInt32(0);
                boxes[itemVnum] = new ItemBoxDto
                {
                    Id = itemVnum,
                    ItemBoxType = (ItemBoxType)reader.GetInt32(1),
                    MinimumRewards = reader.IsDBNull(2) ? null : reader.GetInt32(2),
                    MaximumRewards = reader.IsDBNull(3) ? null : reader.GetInt32(3),
                    ShowsRaidBoxPanelOnOpen = !reader.IsDBNull(4) && reader.GetBoolean(4),
                    Items = new List<ItemBoxItemDto>()
                };
            }
        }

        using (var cmd = new NpgsqlCommand("SELECT item_vnum, probability, min_original_rare, max_original_rare, generated_amount, generated_vnum, generated_random_rarity, generated_upgrade FROM item_box_items;", conn))
        using (var reader = cmd.ExecuteReader())
        {
            while (reader.Read())
            {
                int itemVnum = reader.GetInt32(0);
                if (!boxes.TryGetValue(itemVnum, out ItemBoxDto box))
                {
                    continue;
                }

                box.Items ??= new List<ItemBoxItemDto>();
                box.Items.Add(new ItemBoxItemDto
                {
                    Probability = reader.GetInt16(1),
                    MinimumOriginalItemRare = reader.GetInt16(2),
                    MaximumOriginalItemRare = reader.GetInt16(3),
                    ItemGeneratedAmount = reader.GetInt16(4),
                    ItemGeneratedVNum = reader.GetInt32(5),
                    ItemGeneratedRandomRarity = !reader.IsDBNull(6) && reader.GetBoolean(6),
                    ItemGeneratedUpgrade = reader.GetByte(7)
                });
            }
        }

        return new List<ItemBoxDto>(boxes.Values);
    }

    public static List<ServerMapDto> LoadServerMaps()
    {
        var maps = new Dictionary<int, ServerMapDto>();
        using var conn = new NpgsqlConnection(BuildConnectionString());
        conn.Open();

        using (var cmd = new NpgsqlCommand("SELECT map_id, map_vnum, map_name_id, map_music_id FROM server_maps;", conn))
        using (var reader = cmd.ExecuteReader())
        {
            while (reader.Read())
            {
                int mapId = reader.GetInt32(0);
                maps[mapId] = new ServerMapDto
                {
                    Id = mapId,
                    MapVnum = reader.GetInt32(1),
                    NameId = reader.GetInt32(2),
                    MusicId = reader.GetInt32(3),
                    Flags = new List<MapFlags>()
                };
            }
        }

        using (var cmd = new NpgsqlCommand("SELECT map_id, flag FROM server_map_flags;", conn))
        using (var reader = cmd.ExecuteReader())
        {
            while (reader.Read())
            {
                int mapId = reader.GetInt32(0);
                if (!maps.TryGetValue(mapId, out ServerMapDto map))
                {
                    continue;
                }

                string flag = reader.GetString(1);
                if (Enum.TryParse(flag, out MapFlags parsed))
                {
                    map.Flags ??= new List<MapFlags>();
                    map.Flags.Add(parsed);
                }
            }
        }

        return new List<ServerMapDto>(maps.Values);
    }

    public static List<PortalDTO> LoadMapPortals()
    {
        var result = new List<PortalDTO>();
        using var conn = new NpgsqlConnection(BuildConnectionString());
        conn.Open();
        using var cmd = new NpgsqlCommand("SELECT destination_map_id, destination_map_x, destination_map_y, source_map_id, source_map_x, source_map_y, type FROM map_portals;", conn);
        using var reader = cmd.ExecuteReader();
        while (reader.Read())
        {
            result.Add(new PortalDTO
            {
                DestinationMapId = reader.GetInt32(0),
                DestinationX = reader.GetInt16(1),
                DestinationY = reader.GetInt16(2),
                SourceMapId = reader.GetInt32(3),
                SourceX = reader.GetInt16(4),
                SourceY = reader.GetInt16(5),
                Type = reader.GetInt16(6),
                IsDisabled = false
            });
        }

        return result;
    }
}
