﻿using System.Security.Claims;
using KernelRecordsMVC.Application.ViewModels;
using KernelRecordsMVC.Infrastructure.Data;
using KernelRecordsMVC.Models;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace KernelRecordsMVC.Web.Controllers;

public class AccountController : Controller
{
    private readonly KernelRecordsContext _context;

    public AccountController(KernelRecordsContext context)
    {
        _context = context;
    }


    // =========================================================
    // REGISTER - GET
    // =========================================================

    [HttpGet]
    public IActionResult Register()
    {
        return View();
    }


    // =========================================================
    // REGISTER - POST
    // =========================================================

    [HttpPost]
    [ValidateAntiForgeryToken]
    public IActionResult Register(RegisterViewModel model)
    {
        if (!ModelState.IsValid)
            return View(model);

        if (_context.Users.Any(x => x.Username == model.Username))
        {
            ModelState.AddModelError(
                "Username",
                "Username is already taken.");

            return View(model);
        }

        if (_context.Users.Any(x => x.Email == model.Email))
        {
            ModelState.AddModelError(
                "Email",
                "Email is already registered.");

            return View(model);
        }

        var user = new User
        {
            Email = model.Email,
            Username = model.Username,

            // TODO:
            // Replace with password hashing later.
            Password = model.Password,

            DateCreated = DateTime.Today,
            ShippingAddress = model.ShippingAddress,
            TelephoneNumber = model.TelephoneNumber
        };

        _context.Users.Add(user);

        _context.SaveChanges();


        // Every normal registered user is a Consumer.
        var consumer = new Consumer
        {
            UserId = user.UserId,
            PointsCollected = 0
        };

        _context.Consumers.Add(consumer);

        _context.SaveChanges();

        return RedirectToAction(nameof(Login));
    }


    // =========================================================
    // LOGIN - GET
    // =========================================================

    [HttpGet]
    public IActionResult Login()
    {
        return View();
    }


    // =========================================================
    // LOGIN - POST
    // =========================================================

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Login(
        LoginViewModel model)
    {
        if (!ModelState.IsValid)
            return View(model);

        var user = _context.Users
            .FirstOrDefault(x =>
                x.Username == model.Username &&
                x.Password == model.Password);

        if (user == null)
        {
            ModelState.AddModelError(
                "",
                "Invalid username or password.");

            return View(model);
        }


        // =====================================================
        // DETERMINE ACCOUNT TYPE
        // =====================================================

        var admin = _context.Admins
            .FirstOrDefault(x => x.UserId == user.UserId);

        string role;

        if (admin != null)
        {
            role = "Admin";
        }
        else
        {
            role = "Consumer";
        }


        // =====================================================
        // CREATE CLAIMS
        // =====================================================

        var claims = new List<Claim>
        {
            new Claim(
                ClaimTypes.NameIdentifier,
                user.UserId.ToString()),

            new Claim(
                ClaimTypes.Name,
                user.Username),

            new Claim(
                ClaimTypes.Email,
                user.Email),

            new Claim(
                ClaimTypes.Role,
                role)
        };


        if (admin != null)
        {
            claims.Add(
                new Claim(
                    "AdminType",
                    admin.Type.ToString()));
        }


        var identity = new ClaimsIdentity(
            claims,
            "Cookies");

        var principal =
            new ClaimsPrincipal(identity);


        // =====================================================
        // SIGN IN
        // =====================================================

        await HttpContext.SignInAsync(
            "Cookies",
            principal);


        // =====================================================
        // KEEP SESSION FOR EXISTING CODE
        // =====================================================

        HttpContext.Session.SetInt32(
            "UserId",
            checked((int)user.UserId));

        HttpContext.Session.SetString(
            "Username",
            user.Username);

        HttpContext.Session.SetString(
            "Role",
            role);

        if (admin != null)
        {
            HttpContext.Session.SetString(
                "AdminType",
                admin.Type.ToString());
        }


        return RedirectToAction(
            "Index",
            "Home");
    }


    // =========================================================
    // PROFILE - GET
    // =========================================================

    [HttpGet]
    public IActionResult Profile()
    {
        // Get logged-in user's ID from authentication claims.
        var userIdClaim = User.FindFirst(
            ClaimTypes.NameIdentifier);

        if (userIdClaim == null)
            return RedirectToAction(nameof(Login));

        if (!long.TryParse(
                userIdClaim.Value,
                out var userId))
        {
            return RedirectToAction(nameof(Login));
        }


        // Load user + consumer information.
        var user = _context.Users
            .Include(x => x.Consumer)
            .FirstOrDefault(x =>
                x.UserId == userId);

        if (user == null)
            return NotFound();


        var model = new ProfileViewModel
        {
            UserId = user.UserId,

            Username = user.Username,

            Email = user.Email,

            TelephoneNumber =
                user.TelephoneNumber,

            ShippingAddress =
                user.ShippingAddress,

            DateCreated =
                user.DateCreated,

            PointsCollected =
                user.Consumer?.PointsCollected ?? 0
        };


        return View(model);
    }


    // =========================================================
    // PROFILE - POST
    // =========================================================

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Profile(
        ProfileViewModel model)
    {
        // Get the currently authenticated user.
        var userIdClaim = User.FindFirst(
            ClaimTypes.NameIdentifier);

        if (userIdClaim == null)
            return RedirectToAction(nameof(Login));

        if (!long.TryParse(
                userIdClaim.Value,
                out var userId))
        {
            return RedirectToAction(nameof(Login));
        }


        if (!ModelState.IsValid)
            return View(model);


        var user = _context.Users
            .FirstOrDefault(x =>
                x.UserId == userId);

        if (user == null)
            return NotFound();


        // =====================================================
        // CHECK EMAIL
        // =====================================================

        var emailExists = _context.Users.Any(x =>
            x.Email == model.Email &&
            x.UserId != userId);

        if (emailExists)
        {
            ModelState.AddModelError(
                nameof(model.Email),
                "This email is already registered.");

            return View(model);
        }


        // =====================================================
        // UPDATE USER
        // =====================================================

        user.Email = model.Email;

        user.TelephoneNumber =
            model.TelephoneNumber;

        user.ShippingAddress =
            model.ShippingAddress;


        _context.SaveChanges();


        TempData["Success"] =
            "Your profile has been updated successfully.";


        // =====================================================
        // REFRESH EMAIL CLAIM
        // =====================================================

        var claims = new List<Claim>
        {
            new Claim(
                ClaimTypes.NameIdentifier,
                user.UserId.ToString()),

            new Claim(
                ClaimTypes.Name,
                user.Username),

            new Claim(
                ClaimTypes.Email,
                user.Email),

            new Claim(
                ClaimTypes.Role,
                User.IsInRole("Admin")
                    ? "Admin"
                    : "Consumer")
        };


        var identity = new ClaimsIdentity(
            claims,
            "Cookies");

        var principal =
            new ClaimsPrincipal(identity);


        await HttpContext.SignInAsync(
            "Cookies",
            principal);


        // Keep session synchronized.
        HttpContext.Session.SetString(
            "Username",
            user.Username);


        return RedirectToAction(nameof(Profile));
    }


    // =========================================================
    // LOGOUT
    // =========================================================

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Logout()
    {
        await HttpContext.SignOutAsync("Cookies");

        HttpContext.Session.Clear();

        return RedirectToAction(
            "Index",
            "Home");
    }
}