using Npgsql;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using PhilExampleCrawler.DataBase.Models;
using System.Data;

namespace PhilExampleCrawler.DataBase
{
    //TODO: Could be made static actually
    public class UserAccess
    {
        private readonly string _dbconn;

        public UserAccess(DBSettings dbSettings)
        {
            _dbconn = dbSettings.ToString();
        }

        public async Task<DB_User?> AddUserAsync(string authCode, string phone, bool optin_telegram)
        {
            DB_User? u = null;
            try
            {
                using var conn = new NpgsqlConnection(_dbconn);
                conn.Open();

                var cmd = conn.CreateCommand();
                cmd.CommandText = SQL_InsertUser(authCode, phone, optin_telegram);
                if (await cmd.ExecuteScalarAsync() is int userID)
                    u = new DB_User(userID, authCode, phone, optin_telegram, null, DateTime.Now);

                conn.Close();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
                //TODO: log
            }
            return u;
        }

        public async Task<DB_User?> GetUserAsync(int userID)
        {
            DB_User? user = null;
            try
            {
                using var conn = new NpgsqlConnection(_dbconn);
                conn.Open();

                var cmd = conn.CreateCommand();
                cmd.CommandText = SQL_SelectUser(userID);
                using (var dr = await cmd.ExecuteReaderAsync())
                    while (await dr.ReadAsync())
                        user = ReadUser(dr);

                if (user != null)
                {
                    cmd.CommandText = SQL_SELECT_CrawlSessions(user.ID);
                    using var dr = await cmd.ExecuteReaderAsync();
                    while (await dr.ReadAsync())
                        user.CrawlSessions.Add(ReadCrawlSession(dr, user.ID));
                }

                conn.Close();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
                //TODO: log
            }
            return user;
        }

        public async Task<List<DB_User>> GetUsersAsync()
        {
            List<DB_User> users = new();
            try
            {
                using var conn = new NpgsqlConnection(_dbconn);
                conn.Open();

                var cmd = conn.CreateCommand();
                cmd.CommandText = SQL_SELECT_Users();
                using (var dr = await cmd.ExecuteReaderAsync())
                    while (await dr.ReadAsync())
                        users.Add(ReadUser(dr));

                foreach (var user in users) {
                    cmd.CommandText = SQL_SELECT_CrawlSessions(user.ID);
                    using var dr = await cmd.ExecuteReaderAsync();
                    while (await dr.ReadAsync())
                        user.CrawlSessions.Add(ReadCrawlSession(dr, user.ID));
                }

                conn.Close();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
                //TODO: log
            }
            return users;
        }

        private static DB_User ReadUser(NpgsqlDataReader dr)
            => new(id: dr.GetInt32("id"),
                   auth_code: dr.GetString("auth_code"),
                   phone: dr.GetString("phone"),
                   optin_telegram: dr.GetBoolean("optin_telegram"),
                   has_telegram: dr.ReadNullableBoolean("has_telegram"),
                   createDate: dr.GetDateTime("create_date"));

        private static DB_CrawlSession ReadCrawlSession(NpgsqlDataReader dr, int userID)
            => new(id: dr.GetInt32("id"),
                   user_id: userID,
                   keywords: dr.GetString("keywords"),
                   location_text: dr.GetString("location_text"),
                   category_id: dr.GetInt32("category_id"),
                   radius_km: dr.GetInt32("radius_km"),
                   minPrice: dr.GetInt32("min_price"),
                   maxPrice: dr.GetInt32("max_price"),
                   isPrivate: dr.GetBoolean("is_private"),
                   isCommerial: dr.GetBoolean("is_commercial"));




        #region SQLQueries
        private static string SQL_InsertUser(string authCode, string phone, bool optin_telegram) 
                                                             => $"INSERT INTO " +
                                                                    "phEx_user(auth_code" +
                                                                           ",phone" +
                                                                           ",optin_telegram)" +
                                                                 " VALUES " +
                                                                      "(" +
                                                                     $"'{authCode}'" +
                                                                    $",'{phone}'" +
                                                                    $",{optin_telegram})" +
                                                                 " RETURNING id;";

        private static string SQL_SelectUser(int userID) => $"SELECT " +
                                                                     "id," +
                                                                     "auth_code," +
                                                                     "phone," +
                                                                     "optin_telegram," +
                                                                     "has_telegram," +
                                                                     "create_date" +
                                                            " FROM " +
                                                                     "phEx_user" +
                                                            " WHERE " +
                                                                    $"id = {userID};";

        private static string SQL_SELECT_Users() => "SELECT " +
                                                            "id," +
                                                            "auth_code," +
                                                            "phone," +
                                                            "optin_telegram," +
                                                            "has_telegram," +
                                                            "create_date" +
                                                   " FROM " +
                                                             "phEx_user;";

        private static string SQL_SELECT_CrawlSessions(int userID) => "SELECT " +
                                                                              "id," +
                                                                              "keywords," +
                                                                              "location_text," +
                                                                              "category_id," +
                                                                              "radius_km," +
                                                                              "min_price," +
                                                                              "max_price," +
                                                                              "is_private," +
                                                                              "is_commercial" +
                                                                     " FROM " +
                                                                              "crawl_session" +
                                                                     " WHERE " +
                                                                             $"user_id = {userID};";
        #endregion
    }
}
