using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;  // json
using ContactManagementSystem.Models;
using System;
using System.IO;

namespace ContactManagementSystem.Data
{
    /// <summary>
    /// Application database context for Contact Management System
    /// Manages database connection and entity configurations
    /// Uses appsettings.json for connection string configuration
    /// </summary>
    public class ApplicationDbContext : DbContext
    {
        #region DbSet Properties

        /// <summary>
        /// DbSet for Contact entities
        /// Represents the Contacts table in the database
        /// </summary>
        public DbSet<Contact> Contacts { get; set; }

        #endregion

        #region Constructors

        /// <summary>
        /// Default constructor - Uses OnConfiguring with appsettings.json
        /// Used for: Migrations, simple applications, console apps
        /// </summary>
        public ApplicationDbContext()
        {
        }

        /// <summary>
        /// Constructor with options - Supports Dependency Injection
        /// Used for: ASP.NET Core, testability, advanced scenarios
        /// </summary>
        /// <param name="options">DbContext options</param>
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
            : base(options)
        {
        }

        #endregion

        #region Configuration

        /// <summary>
        /// Configures the database connection using appsettings.json
        /// Only used when DbContextOptions not provided via constructor
        /// </summary>
        /// <param name="optionsBuilder">Options builder</param>
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            // Only configure if not already configured (allows DI to override)
            if (!optionsBuilder.IsConfigured)
            {
                // Build configuration from appsettings.json
                var configuration = new ConfigurationBuilder() // Microsoft.Extensions.json
                    .SetBasePath(Directory.GetCurrentDirectory())
                    .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
                    .Build();

                // Get connection string from appsettings.json
                var connectionString = configuration.GetConnectionString("DefaultConnection");

                // Validate connection string exists
                if (string.IsNullOrEmpty(connectionString))
                {
                    throw new InvalidOperationException(
                        "Connection string 'DefaultConnection' not found in appsettings.json. " +
                        "Please ensure appsettings.json exists and contains ConnectionStrings section.");
                }

                // Use Pomelo MySQL provider (recommended for MySQL with EF Core)
                optionsBuilder.UseMySql(
                    connectionString,
                    ServerVersion.AutoDetect(connectionString)
                );

                // Optional: Enable sensitive data logging for development
                // WARNING: Only use in development - exposes sensitive data in logs
#if DEBUG
                optionsBuilder.EnableSensitiveDataLogging();
               // optionsBuilder.LogTo(Console.WriteLine, Microsoft.Extensions.Logging.LogLevel.Information);
#endif
            }
        }

        /// <summary>
        /// Configures entity models and relationships
        /// Called when model is being created
        /// </summary>
        /// <param name="modelBuilder">Model builder</param>
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            #region Contact Entity Configuration

            modelBuilder.Entity<Contact>(entity =>
            {
                // Table name
                entity.ToTable("Contacts");

                // Primary key
                entity.HasKey(c => c.Id);

                // Property configurations
                entity.Property(c => c.Id)
                    .ValueGeneratedOnAdd(); // Auto-increment

                entity.Property(c => c.FirstName)
                    .IsRequired()
                    .HasMaxLength(50)
                    .HasColumnType("varchar(50)");

                entity.Property(c => c.LastName)
                    .IsRequired()
                    .HasMaxLength(50)
                    .HasColumnType("varchar(50)");

                entity.Property(c => c.Email)
                    .IsRequired()
                    .HasMaxLength(100)
                    .HasColumnType("varchar(100)");

                entity.Property(c => c.PhoneNumber)
                    .IsRequired()
                    .HasMaxLength(20)
                    .HasColumnType("varchar(20)");

                entity.Property(c => c.Address)
                    .HasMaxLength(200)
                    .HasColumnType("varchar(200)");

                entity.Property(c => c.Company)
                    .HasMaxLength(100)
                    .HasColumnType("varchar(100)");

                entity.Property(c => c.JobTitle)
                    .HasMaxLength(100)
                    .HasColumnType("varchar(100)");

                entity.Property(c => c.Notes)
                    .HasColumnType("text");

                entity.Property(c => c.IsFavorite)
                    .IsRequired()
                    .HasDefaultValue(false);

                entity.Property(c => c.DateCreated)
                    .IsRequired()
                    .HasDefaultValueSql("CURRENT_TIMESTAMP");

                entity.Property(c => c.DateModified)
                    .IsRequired()
                    .HasDefaultValueSql("CURRENT_TIMESTAMP");

                // Indexes for performance Search terms
                entity.HasIndex(c => c.Email)
                    .IsUnique()
                    .HasDatabaseName("IX_Contacts_Email");

                entity.HasIndex(c => c.LastName)
                    .HasDatabaseName("IX_Contacts_LastName");

                entity.HasIndex(c => new { c.LastName, c.FirstName })
                    .HasDatabaseName("IX_Contacts_LastName_FirstName");

                entity.HasIndex(c => c.Company)
                    .HasDatabaseName("IX_Contacts_Company");

                entity.HasIndex(c => c.IsFavorite)
                    .HasDatabaseName("IX_Contacts_IsFavorite");
            });

            #endregion

            #region Seed Data

            // Seed initial data for testing and demonstration
            /*
            modelBuilder.Entity<Contact>().HasData(
                new Contact
                {
                    Id = 1,
                    FirstName = "John",
                    LastName = "Doe",
                    Email = "john.doe@example.com",
                    PhoneNumber = "555-0101",
                    Address = "123 Main St, Springfield, IL 62701",
                    Company = "Tech Corp",
                    JobTitle = "Software Developer",
                    Notes = "Primary contact for backend development projects",
                    IsFavorite = true,
                    DateCreated = new DateTime(2024, 1, 15, 10, 30, 0),
                    DateModified = new DateTime(2024, 1, 15, 10, 30, 0)
                },
                new Contact
                {
                    Id = 2,
                    FirstName = "Jane",
                    LastName = "Smith",
                    Email = "jane.smith@example.com",
                    PhoneNumber = "555-0102",
                    Address = "456 Oak Ave, Portland, OR 97201",
                    Company = "Design Studio",
                    JobTitle = "UI/UX Designer",
                    Notes = "Excellent designer, available for freelance work",
                    IsFavorite = false,
                    DateCreated = new DateTime(2024, 2, 10, 14, 15, 0),
                    DateModified = new DateTime(2024, 2, 10, 14, 15, 0)
                },
                new Contact
                {
                    Id = 3,
                    FirstName = "Robert",
                    LastName = "Johnson",
                    Email = "robert.johnson@example.com",
                    PhoneNumber = "555-0103",
                    Address = "789 Pine Rd, Austin, TX 78701",
                    Company = "Tech Corp",
                    JobTitle = "Project Manager",
                    Notes = "Manages all client-facing projects",
                    IsFavorite = true,
                    DateCreated = new DateTime(2024, 3, 5, 9, 0, 0),
                    DateModified = new DateTime(2024, 3, 5, 9, 0, 0)
                }
            );
            */

            #endregion
        } // method()

        #endregion

        #region Helper Methods

        /// <summary>
        /// Ensures the database is created (for simple scenarios)
        /// NOTE: Use migrations for production!
        /// </summary>
        public void EnsureDatabaseCreated()
        {
            Database.EnsureCreated();
        }

        /// <summary>
        /// Applies any pending migrations
        /// </summary>
        public void ApplyMigrations()
        {
            Database.Migrate();
        }

        /// <summary>
        /// Tests if the database connection is working
        /// </summary>
        /// <returns>True if connection successful, false otherwise</returns>
        public bool TestConnection()
        {
            try
            {
                return Database.CanConnect();
            }
            catch
            {
                return false;
            }
        }

        #endregion
    }
}

/* INSTRUCTOR NOTES FOR DAY 2:
 * 
 * ========================================
 * KEY CONCEPTS TO EXPLAIN:
 * ========================================
 * 
 * 1. Reading from appsettings.json:
 *    - ConfigurationBuilder loads the JSON file
 *    - SetBasePath(Directory.GetCurrentDirectory()) finds the file
 *    - GetConnectionString("DefaultConnection") reads the value
 *    - Much better than hardcoding - can change without recompiling!
 * 
 * 2. Why Use Configuration Files?
 *    - Separation of concerns (config separate from code)
 *    - Different settings per environment (dev, test, prod)
 *    - Security: passwords not in source code
 *    - Easy to modify without rebuilding application
 * 
 * 3. Required NuGet Packages:
 *    - Microsoft.EntityFrameworkCore (8.0.0)
 *    - Microsoft.EntityFrameworkCore.Tools (8.0.0)
 *    - Pomelo.EntityFrameworkCore.MySql (8.0.0)
 *    - Microsoft.EntityFrameworkCore.Design (8.0.0)
 *    - Microsoft.Extensions.Configuration (8.0.0)  ← NEW!
 *    - Microsoft.Extensions.Configuration.Json (8.0.0)  ← NEW!
 * 
 * 4. appsettings.json Structure:
 *    {
 *      "ConnectionStrings": {
 *        "DefaultConnection": "server=localhost;database=ContactManagementDB;..."
 *      }
 *    }
 * 
 * 5. File Must Be Copied to Output:
 *    - In Solution Explorer, select appsettings.json
 *    - Press F4 (Properties)
 *    - Set "Copy to Output Directory" = "Copy if newer"
 *    - Without this, file won't be found at runtime!
 * 
 * 6. Error Handling:
 *    - Code checks if connection string is null/empty
 *    - Throws clear error message if missing
 *    - Helps students debug configuration issues
 * 
 * 7. Two Constructor Patterns:
 *    - Parameterless constructor: Uses OnConfiguring (console apps)
 *    - Constructor with options: Uses DI (ASP.NET Core)
 *    - OnConfiguring only runs if options not already configured
 * 
 * 8. Development vs Production:
 *    - #if DEBUG enables sensitive logging only in dev
 *    - Production should use environment variables or Azure Key Vault
 *    - Never commit passwords to source control!
 * 
 * ========================================
 * COMMON STUDENT ISSUES:
 * ========================================
 * 
 * Issue 1: "Could not find file 'appsettings.json'"
 * Solution: Set "Copy to Output Directory" to "Copy if newer"
 * 
 * Issue 2: "Connection string 'DefaultConnection' not found"
 * Solution: Check JSON structure, ensure ConnectionStrings section exists
 * 
 * Issue 3: "Unable to resolve service for type IConfiguration"
 * Solution: Install Microsoft.Extensions.Configuration.Json package
 * 
 * Issue 4: Connection string null
 * Solution: Check appsettings.json file name (case-sensitive on Linux/Mac)
 * 
 * Issue 5: Can't connect to database
 * Solution: Verify MySQL running, check password in appsettings.json
 * 
 * ========================================
 * TEACHING DEMONSTRATION:
 * ========================================
 * 
 * 1. Show hardcoded version first (explain limitations)
 * 2. Introduce appsettings.json (show how it works)
 * 3. Demonstrate reading configuration
 * 4. Show what happens when file is missing
 * 5. Show what happens when "Copy to Output" not set
 * 6. Test connection with different settings
 * 
 * ========================================
 * TESTING THE CONFIGURATION:
 * ========================================
 * 
 * Add this to Program.cs to test:
 * 
 * using ContactManagementSystem.Data;
 * 
 * Console.WriteLine("Testing database connection...\n");
 * 
 * using (var context = new ApplicationDbContext())
 * {
 *     if (context.TestConnection())
 *     {
 *         Console.WriteLine("✓ Connection successful!");
 *         Console.WriteLine($"✓ Database: ContactManagementDB");
 *     }
 *     else
 *     {
 *         Console.WriteLine("✗ Connection failed!");
 *         Console.WriteLine("Check appsettings.json and MySQL server");
 *     }
 * }
 * 
 * ========================================
 * ADVANTAGES OF THIS APPROACH:
 * ========================================
 * 
 * ✓ No hardcoded connection strings
 * ✓ Easy to change without recompiling
 * ✓ Can have different configs for dev/test/prod
 * ✓ Passwords not in source code
 * ✓ Follows industry best practices
 * ✓ Supports multiple connection strings
 * ✓ Clear error messages for troubleshooting
 * 
 * ========================================
 * NEXT STEPS:
 * ========================================
 * 
 * After students understand this:
 * 1. Create first migration (Add-Migration InitialCreate)
 * 2. Update database (Update-Database)
 * 3. Create repository classes
 * 4. Test CRUD operations
 * 5. Build service layer on Day 3
 */