using System;
using System.Text.Json;
using AiLogia.Bot.Services;
using Microsoft.Extensions.Configuration;
using Npgsql;
using Telegram.Bot.Types;

namespace AiLogia.Bot.Diagnostics;

public interface IUserActionTracker
{
    Task TrackAsync(string actionType, Message message, object? payload = null);
    Task TrackCallbackAsync(string actionType, CallbackQuery callback, object? payload = null);
}

public class UserActionTracker(ISystemLogService log, IConfiguration config) : IUserActionTracker
{
    private readonly ISystemLogService _log = log;
    private readonly string _connectionString = config.GetConnectionString("Postgres") ??
                            throw new InvalidOperationException("Missing connection string");

    public async Task TrackAsync(string actionType, Message message, object? payload = null)
    {
        if (message.From == null) return;

        var userId = message.From.Id;
        var username = message.From.Username;
        var fullName = $"{message.From.FirstName} {message.From.LastName}".Trim();
        var msg = message.Text ?? "";
        var payloadJson = payload != null ? JsonSerializer.Serialize(payload) : null;

        await WriteLogAsync(userId, username, fullName, actionType, msg, payloadJson);
    }

    public async Task TrackCallbackAsync(string actionType, CallbackQuery callback, object? payload = null)
    {
        if (callback.From == null) return;

        var userId = callback.From.Id;
        var username = callback.From.Username;
        var fullName = $"{callback.From.FirstName} {callback.From.LastName}".Trim();
        var msg = callback.Data ?? "";
        var payloadJson = payload != null ? JsonSerializer.Serialize(payload) : null;

        await WriteLogAsync(userId, username, fullName, actionType, msg, payloadJson);
    }

    private async Task WriteLogAsync(long userId, string? username, string fullName, string actionType, string message, string? payloadJson)
    {
        string? audience;
        string? access;

        try
        {
            await using var conn = new NpgsqlConnection(_connectionString);
            await conn.OpenAsync();

            // Try get access info
            var cmdCheck = new NpgsqlCommand("SELECT audience_type, access_level FROM user_access WHERE user_id = @uid", conn);
            cmdCheck.Parameters.AddWithValue("uid", userId);
            await using var reader = await cmdCheck.ExecuteReaderAsync();
            if (await reader.ReadAsync())
            {
                audience = reader.GetString(0);
                access = reader.GetString(1);
            }
            // if no access - create user record
            else
            {
                await reader.CloseAsync();
                var cmdInsert = new NpgsqlCommand("INSERT INTO user_access (user_id, audience_type, access_level) VALUES (@uid, 'b2c', 'free')", conn);
                cmdInsert.Parameters.AddWithValue("uid", userId);
                await cmdInsert.ExecuteNonQueryAsync();
                audience = "b2c";
                access = "free";
            }

            await reader.CloseAsync();

            // Insert log
            var cmd = new NpgsqlCommand(@"
                INSERT INTO user_logs (timestamp, user_id, username, full_name, action_type, message, payload, audience_type, access_level)
                VALUES (now(), @uid, @username, @fullname, @type, @msg, @payload::jsonb, @audience, @access)", conn);
            cmd.Parameters.AddWithValue("uid", userId);
            cmd.Parameters.AddWithValue("username", (object?)username ?? DBNull.Value);
            cmd.Parameters.AddWithValue("fullname", fullName);
            cmd.Parameters.AddWithValue("type", actionType);
            cmd.Parameters.AddWithValue("msg", message);
            cmd.Parameters.AddWithValue("payload", (object?)payloadJson ?? DBNull.Value);
            cmd.Parameters.AddWithValue("audience", (object?)audience ?? DBNull.Value);
            cmd.Parameters.AddWithValue("access", (object?)access ?? DBNull.Value);

            await cmd.ExecuteNonQueryAsync();
        }
        catch (Exception ex)
        {
            await _log.ExceptionAsync(ex, new
            {
                userId,
                actionType,
                message,
                payload = payloadJson
            });
        }
    }
}