﻿using KernelRecordsMVC.Domain.Enums;
using KernelRecordsMVC.Domain.Models;
using KernelRecordsMVC.Models;
using Microsoft.EntityFrameworkCore;

namespace KernelRecordsMVC.Infrastructure.Data;

public class KernelRecordsContext : DbContext
{
    public KernelRecordsContext(DbContextOptions<KernelRecordsContext> options)
        : base(options)
    {
    }

    public DbSet<User> Users => Set<User>();
    public DbSet<Admin> Admins => Set<Admin>();
    public DbSet<Consumer> Consumers => Set<Consumer>();

    public DbSet<Artist> Artists => Set<Artist>();
    public DbSet<Release> Releases => Set<Release>();
    public DbSet<Album> Albums => Set<Album>();
    public DbSet<SingleRelease> SingleReleases => Set<SingleRelease>();
    public DbSet<Song> Songs => Set<Song>();

    public DbSet<Product> Products => Set<Product>();

    public DbSet<Order> Orders => Set<Order>();
    public DbSet<OrderProduct> OrderProducts => Set<OrderProduct>();

    public DbSet<Wishlist> Wishlists => Set<Wishlist>();
    public DbSet<WishlistProduct> WishlistProducts => Set<WishlistProduct>();

    public DbSet<Modification> Modifications => Set<Modification>();
    public DbSet<ModificationProduct> ModificationProducts
        => Set<ModificationProduct>();

    public DbSet<ReleaseArtist> ReleaseArtists => Set<ReleaseArtist>();
    public DbSet<SongArtist> SongArtists => Set<SongArtist>();
    public DbSet<AlbumSong> AlbumSongs => Set<AlbumSong>();

    /*
    public DbSet<SingleFeature> SingleFeatures => Set<SingleFeature>();
    */

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        // ==========================================
        // DATABASE SCHEMA
        // ==========================================

        modelBuilder.HasDefaultSchema("project");


        // ==========================================
        // POSTGRESQL ENUMS
        // ==========================================

        modelBuilder.HasPostgresEnum<AdminType>("project", "admin_type");
        modelBuilder.HasPostgresEnum<ProductFormat>("project", "product_format");
        modelBuilder.HasPostgresEnum<PaymentMethodType>("project", "payment_method_type");
        modelBuilder.HasPostgresEnum<OrderStatusType>("project", "order_status_type");
        modelBuilder.HasPostgresEnum<ModificationType>("project", "modification_type");
        modelBuilder.HasPostgresEnum<ArtistReleaseType>("project", "artist_release_type");


        // ==========================================
        // USERS
        // ==========================================

        modelBuilder.Entity<User>(entity =>
        {
            entity.ToTable("users");

            entity.HasKey(x => x.UserId);

            entity.Property(x => x.UserId)
                .HasColumnName("user_id")
                .ValueGeneratedOnAdd();

            entity.Property(x => x.Email)
                .HasColumnName("email")
                .IsRequired();

            entity.Property(x => x.Username)
                .HasColumnName("username")
                .IsRequired();

            entity.Property(x => x.Password)
                .HasColumnName("password")
                .IsRequired();

            entity.Property(x => x.DateCreated)
                .HasColumnName("date_created")
                .HasColumnType("date");

            entity.Property(x => x.ShippingAddress)
                .HasColumnName("shipping_address");

            entity.Property(x => x.TelephoneNumber)
                .HasColumnName("telephone_number");

            entity.HasIndex(x => x.Email)
                .IsUnique();

            entity.HasIndex(x => x.Username)
                .IsUnique();
        });


        // ==========================================
        // ADMINS
        // ==========================================

        modelBuilder.Entity<Admin>(entity =>
        {
            entity.ToTable("admins");

            entity.HasKey(x => x.UserId);

            entity.Property(x => x.UserId)
                .HasColumnName("user_id");

            entity.Property(x => x.Type)
                .HasColumnName("type")
                .HasColumnType("project.admin_type");

            entity.Property(x => x.DiscountPercentage)
                .HasColumnName("discount_percentage")
                .HasColumnType("numeric");

            entity.HasOne(x => x.User)
                .WithOne(x => x.Admin)
                .HasForeignKey<Admin>(x => x.UserId)
                .OnDelete(DeleteBehavior.Cascade);
        });


        // ==========================================
        // CONSUMERS
        // ==========================================

        modelBuilder.Entity<Consumer>(entity =>
        {
            entity.ToTable("consumers");

            entity.HasKey(x => x.UserId);

            entity.Property(x => x.UserId)
                .HasColumnName("user_id");

            entity.Property(x => x.PointsCollected)
                .HasColumnName("points_collected");

            entity.HasOne(x => x.User)
                .WithOne(x => x.Consumer)
                .HasForeignKey<Consumer>(x => x.UserId)
                .OnDelete(DeleteBehavior.Cascade);
        });


        // ==========================================
        // ARTISTS
        // ==========================================

        modelBuilder.Entity<Artist>(entity =>
        {
            entity.ToTable("artists");

            entity.HasKey(x => x.ArtistId);

            entity.Property(x => x.ArtistId)
                .HasColumnName("artist_id");

            entity.Property(x => x.ArtistName)
                .HasColumnName("artist_name")
                .IsRequired();

            entity.Property(x => x.ArtistDescription)
                .HasColumnName("artist_description");

            entity.Property(x => x.ArtistPhoto)
                .HasColumnName("artist_photo");
        });


        // ==========================================
        // RELEASES
        // ==========================================

        modelBuilder.Entity<Release>(entity =>
        {
            entity.ToTable("releases");

            entity.HasKey(x => x.ReleaseId);

            entity.Property(x => x.ReleaseId)
                .HasColumnName("release_id");

            entity.Property(x => x.Title)
                .HasColumnName("title")
                .IsRequired();

            entity.Property(x => x.RecordLabel)
                .HasColumnName("record_label");

            entity.Property(x => x.Genre)
                .HasColumnName("genre")
                .IsRequired();

            entity.Property(x => x.ReleaseDate)
                .HasColumnName("release_date")
                .HasColumnType("date");

            entity.Property(x => x.CoverPhoto)
                .HasColumnName("cover_photo")
                .IsRequired();
        });


        // ==========================================
        // ALBUMS
        // ==========================================

        modelBuilder.Entity<Album>(entity =>
        {
            entity.ToTable("albums");

            entity.HasKey(x => x.ReleaseId);

            entity.Property(x => x.ReleaseId)
                .HasColumnName("release_id");

            entity.HasOne(x => x.Release)
                .WithOne(x => x.Album)
                .HasForeignKey<Album>(x => x.ReleaseId)
                .OnDelete(DeleteBehavior.Cascade);
        });


        // ==========================================
        // SINGLE RELEASES
        // ==========================================

        modelBuilder.Entity<SingleRelease>(entity =>
        {
            entity.ToTable("single_releases");

            entity.HasKey(x => x.ReleaseId);

            entity.Property(x => x.ReleaseId)
                .HasColumnName("release_id");

            entity.Property(x => x.Duration)
                .HasColumnName("duration")
                .IsRequired();

            entity.HasOne(x => x.Release)
                .WithOne(x => x.SingleRelease)
                .HasForeignKey<SingleRelease>(x => x.ReleaseId)
                .OnDelete(DeleteBehavior.Cascade);
        });


        // ==========================================
        // SONGS
        // ==========================================

        modelBuilder.Entity<Song>(entity =>
        {
            entity.ToTable("songs");

            entity.HasKey(x => x.SongId);

            entity.Property(x => x.SongId)
                .HasColumnName("song_id");

            entity.Property(x => x.SongName)
                .HasColumnName("song_name")
                .IsRequired();

            entity.Property(x => x.SongDuration)
                .HasColumnName("song_duration")
                .IsRequired();
        });


        // ==========================================
        // PRODUCTS
        // ==========================================

        modelBuilder.Entity<Product>(entity =>
        {
            entity.ToTable("products");

            entity.HasKey(x => x.ProductId);

            entity.Property(x => x.ProductId)
                .HasColumnName("product_id");

            entity.Property(x => x.ReleaseId)
                .HasColumnName("release_id");

            entity.Property(x => x.Format)
                .HasColumnName("format")
                .HasColumnType("project.product_format");

            entity.Property(x => x.Price)
                .HasColumnName("price")
                .HasColumnType("numeric");

            entity.Property(x => x.ProductDescription)
                .HasColumnName("product_description")
                .IsRequired();

            entity.Property(x => x.Stock)
                .HasColumnName("stock");

            entity.HasOne(x => x.Release)
                .WithMany(x => x.Products)
                .HasForeignKey(x => x.ReleaseId)
                .OnDelete(DeleteBehavior.Cascade);
        });


        // ==========================================
        // ORDERS
        // ==========================================

        modelBuilder.Entity<Order>(entity =>
        {
            entity.ToTable("orders");

            entity.HasKey(x => x.OrderId);

            entity.Property(x => x.OrderId)
                .HasColumnName("order_id")
                .ValueGeneratedOnAdd();

            entity.Property(x => x.UserId)
                .HasColumnName("user_id");

            entity.Property(x => x.PaymentMethod)
                .HasColumnName("payment_method")
                .HasColumnType("project.payment_method_type");

            entity.Property(x => x.PurchaseDate)
                .HasColumnName("purchase_date")
                .HasColumnType("date");

            entity.Property(x => x.PointsEarned)
                .HasColumnName("points_earned");

            entity.Property(x => x.PointsUsed)
                .HasColumnName("points_used");

            entity.Property(x => x.Status)
                .HasColumnName("status")
                .HasColumnType("project.order_status_type");

            entity.HasOne(x => x.User)
                .WithMany(x => x.Orders)
                .HasForeignKey(x => x.UserId)
                .OnDelete(DeleteBehavior.Cascade);
        });


        // ==========================================
        // ORDER_PRODUCTS
        // ==========================================

        modelBuilder.Entity<OrderProduct>(entity =>
        {
            entity.ToTable("order_products");

            entity.HasKey(x => new
            {
                x.OrderId,
                x.ProductId
            });

            entity.Property(x => x.OrderId)
                .HasColumnName("order_id");

            entity.Property(x => x.ProductId)
                .HasColumnName("product_id");

            entity.Property(x => x.PriceAtPurchase)
                .HasColumnName("price_at_purchase")
                .HasColumnType("numeric");

            entity.Property(x => x.Quantity)
                .HasColumnName("quantity");

            entity.HasOne(x => x.Order)
                .WithMany(x => x.OrderProducts)
                .HasForeignKey(x => x.OrderId)
                .OnDelete(DeleteBehavior.Cascade);

            entity.HasOne(x => x.Product)
                .WithMany(x => x.OrderProducts)
                .HasForeignKey(x => x.ProductId)
                .OnDelete(DeleteBehavior.Cascade);
        });


        // ==========================================
        // WISHLISTS
        // ==========================================

        modelBuilder.Entity<Wishlist>(entity =>
        {
            entity.ToTable("wishlists");

            entity.HasKey(x => x.WishlistId);

            entity.Property(x => x.WishlistId)
                .HasColumnName("wishlist_id");

            entity.Property(x => x.UserId)
                .HasColumnName("user_id");

            entity.HasIndex(x => x.UserId)
                .IsUnique();

            entity.HasOne(x => x.User)
                .WithOne(x => x.Wishlist)
                .HasForeignKey<Wishlist>(x => x.UserId)
                .OnDelete(DeleteBehavior.Cascade);
        });


        // ==========================================
        // WISHLIST_PRODUCTS
        // ==========================================

        modelBuilder.Entity<WishlistProduct>(entity =>
        {
            entity.ToTable("wishlist_products");

            entity.HasKey(x => new
            {
                x.WishlistId,
                x.ProductId
            });

            entity.Property(x => x.WishlistId)
                .HasColumnName("wishlist_id");

            entity.Property(x => x.ProductId)
                .HasColumnName("product_id");

            entity.Property(x => x.AddedAt)
                .HasColumnName("added_at")
                .HasColumnType("date");

            entity.HasOne(x => x.Wishlist)
                .WithMany(x => x.WishlistProducts)
                .HasForeignKey(x => x.WishlistId)
                .OnDelete(DeleteBehavior.Cascade);

            entity.HasOne(x => x.Product)
                .WithMany(x => x.WishlistProducts)
                .HasForeignKey(x => x.ProductId)
                .OnDelete(DeleteBehavior.Cascade);
        });


        // ==========================================
        // MODIFICATIONS
        // ==========================================

        modelBuilder.Entity<Modification>(entity =>
        {
            entity.ToTable("modifications");

            entity.HasKey(x => x.ModificationId);

            entity.Property(x => x.ModificationId)
                .HasColumnName("modification_id");

            entity.Property(x => x.AdminId)
                .HasColumnName("admin_id");

            entity.Property(x => x.DateModified)
                .HasColumnName("date_modified")
                .HasColumnType("date");

            entity.Property(x => x.TypeOfModification)
                .HasColumnName("type_of_modification")
                .HasColumnType("project.modification_type");

            entity.Property(x => x.Discount)
                .HasColumnName("discount")
                .HasColumnType("numeric");

            entity.HasOne(x => x.Admin)
                .WithMany(x => x.Modifications)
                .HasForeignKey(x => x.AdminId)
                .OnDelete(DeleteBehavior.Cascade);
        });


        // ==========================================
        // MODIFICATION_PRODUCTS
        // ==========================================

        modelBuilder.Entity<ModificationProduct>(entity =>
        {
            entity.ToTable("modification_products");

            entity.HasKey(x => new
            {
                x.ModificationId,
                x.ProductId
            });

            entity.Property(x => x.ModificationId)
                .HasColumnName("modification_id");

            entity.Property(x => x.ProductId)
                .HasColumnName("product_id");

            entity.HasOne(x => x.Modification)
                .WithMany(x => x.ModificationProducts)
                .HasForeignKey(x => x.ModificationId)
                .OnDelete(DeleteBehavior.Cascade);

            entity.HasOne(x => x.Product)
                .WithMany(x => x.ModificationProducts)
                .HasForeignKey(x => x.ProductId)
                .OnDelete(DeleteBehavior.Cascade);
        });


        // ==========================================
        // RELEASE_ARTISTS
        // ==========================================

        modelBuilder.Entity<ReleaseArtist>(entity =>
        {
            entity.ToTable("release_artists");

            entity.HasKey(x => new
            {
                x.ReleaseId,
                x.ArtistId
            });

            entity.Property(x => x.ReleaseId)
                .HasColumnName("release_id");

            entity.Property(x => x.ArtistId)
                .HasColumnName("artist_id");

            entity.Property(x => x.ReleaseOrdinal)
                .HasColumnName("release_ordinal");

            entity.Property(x => x.Type)
                .HasColumnName("type")
                .HasColumnType("project.artist_release_type");

            entity.HasOne(x => x.Release)
                .WithMany(x => x.ReleaseArtists)
                .HasForeignKey(x => x.ReleaseId)
                .OnDelete(DeleteBehavior.Cascade);

            entity.HasOne(x => x.Artist)
                .WithMany(x => x.ReleaseArtists)
                .HasForeignKey(x => x.ArtistId)
                .OnDelete(DeleteBehavior.Cascade);
        });


        // ==========================================
        // SONG_ARTISTS
        // ==========================================

        modelBuilder.Entity<SongArtist>(entity =>
        {
            entity.ToTable("song_artists");

            entity.HasKey(x => new
            {
                x.SongId,
                x.ArtistId
            });

            entity.Property(x => x.SongId)
                .HasColumnName("song_id");

            entity.Property(x => x.ArtistId)
                .HasColumnName("artist_id");

            entity.Property(x => x.SongOrdinal)
                .HasColumnName("song_ordinal");

            entity.HasOne(x => x.Song)
                .WithMany(x => x.SongArtists)
                .HasForeignKey(x => x.SongId)
                .OnDelete(DeleteBehavior.Cascade);

            entity.HasOne(x => x.Artist)
                .WithMany(x => x.SongArtists)
                .HasForeignKey(x => x.ArtistId)
                .OnDelete(DeleteBehavior.Cascade);
        });


        // ==========================================
        // ALBUM_SONGS
        // ==========================================

        modelBuilder.Entity<AlbumSong>(entity =>
        {
            entity.ToTable("album_songs");

            entity.HasKey(x => new
            {
                x.AlbumId,
                x.SongId
            });

            entity.Property(x => x.AlbumId)
                .HasColumnName("album_id");

            entity.Property(x => x.SongId)
                .HasColumnName("song_id");

            entity.HasOne(x => x.Album)
                .WithMany(x => x.AlbumSongs)
                .HasForeignKey(x => x.AlbumId)
                .OnDelete(DeleteBehavior.Cascade);

            entity.HasOne(x => x.Song)
                .WithMany(x => x.AlbumSongs)
                .HasForeignKey(x => x.SongId)
                .OnDelete(DeleteBehavior.Cascade);
        });


        /*// ==========================================
        // SINGLE_FEATURES
        // ==========================================

        modelBuilder.Entity<SingleFeature>(entity =>
        {
            entity.ToTable("single_features");

            entity.HasKey(x => new
            {
                x.SingleId,
                x.SongId
            });

            entity.Property(x => x.SingleId)
                .HasColumnName("single_id");

            entity.Property(x => x.SongId)
                .HasColumnName("song_id");

            entity.HasOne(x => x.Single)
                .WithMany(x => x.SingleFeatures)
                .HasForeignKey(x => x.SingleId)
                .OnDelete(DeleteBehavior.Cascade);

            entity.HasOne(x => x.Song)
                .WithMany(x => x.SingleFeatures)
                .HasForeignKey(x => x.SongId)
                .OnDelete(DeleteBehavior.Cascade);
        });*/
    }
}