using System.Text.Json.Serialization;
using ERPCore.Infra.Auth;
using ERPCore.Infra.Auth.AuthHex;
using ERPCore.Infra.Gl;
using ERPCore.Infra.Persistence;
using ERPCore.Infra.Storage;
using ERPCore.Infra.UoW;
using ERPCore.Repositories;
using ERPCore.Repositories.Interfaces;
using ERPCore.Services;
using ERPCore.Services.Interfaces;
using ERPCore.Services.Auth;
using ERPCore.Services.Hrm;
using ERPCore.Services.Production;
using ERPCore.Services.Stock;
using ERPCore.System.Errors;
using Microsoft.AspNetCore.Authentication;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.OpenApi;
using Npgsql;
using Serilog;

var builder = WebApplication.CreateBuilder(args);

// Serilog (file sink) — reads levels from configuration, plus a rolling daily file.
builder.Host.UseSerilog((ctx, cfg) => cfg
    .ReadFrom.Configuration(ctx.Configuration)
    .WriteTo.File("logs/erpcore-.log", rollingInterval: RollingInterval.Day));

// Controllers + JSON: serialize enums as their string names (docs/11 §8, camelCase).
builder.Services.AddControllers()
    .AddJsonOptions(o => o.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()));

// EF Core + PostgreSQL
builder.Services.AddDbContext<ErpDbContext>(o =>
{
    o.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection"));
    o.ConfigureWarnings(w => w.Ignore(RelationalEventId.PendingModelChangesWarning));
});

// ProblemDetails (RFC 7807) + domain-exception mapping
builder.Services.AddProblemDetails();
builder.Services.AddExceptionHandler<DomainExceptionHandler>();

// Auth: validate external AuthHex RS256 tokens + ERP door policy (docs/10 A.4)
builder.Services.AddErpJwtAuth(builder.Configuration);

// AuthController proxy → AuthHex (docs/11 §2.0)
builder.Services.AddHttpClient<IAuthHexClient, AuthHexClient>(c =>
{
    var baseUrl = builder.Configuration["AuthHex:BaseUrl"]
        ?? throw new InvalidOperationException("AuthHex:BaseUrl is not configured.");
    c.BaseAddress = new Uri(baseUrl);
});
builder.Services.AddScoped<IAuthUserService, AuthUserService>();
builder.Services.AddScoped<IAuthRecoveryService, AuthRecoveryService>();
builder.Services.AddScoped<IAuthAltService, AuthAltService>();

// General Ledger service proxy → external GL microservice (docs/12-GENERAL-LEDGER-INTEGRATION.md)
builder.Services.AddHttpClient<IGeneralLedgerClient, GeneralLedgerClient>(c =>
{
    var baseUrl = builder.Configuration["GeneralLedgerService:BaseUrl"]
        ?? throw new InvalidOperationException("GeneralLedgerService:BaseUrl is not configured.");
    c.BaseAddress = new Uri(baseUrl);
});
builder.Services.AddScoped<IGeneralLedgerService, GeneralLedgerService>();

// Current-user (audit actor). AuthHex has no sub/nameid → a claims transformation
// JIT-provisions a local shadow user and injects the local `int` id as `nameid`.
builder.Services.AddHttpContextAccessor();
builder.Services.AddScoped<ICurrentUser, CurrentUser>();
builder.Services.AddScoped<IClaimsTransformation, ShadowUserClaimsTransformation>();

// Unit of work + generic repository base
builder.Services.AddScoped<IUnitOfWork, UnitOfWork>();
builder.Services.AddScoped(typeof(IRepository<>), typeof(Repository<>));

// Master-data services (docs/11 §2)
builder.Services.AddScoped<ICustomerService, CustomerService>();
builder.Services.AddScoped<IItemService, ItemService>();
builder.Services.AddScoped<IUomService, UomService>();
builder.Services.AddScoped<ICategoryService, CategoryService>();
builder.Services.AddScoped<IBrandService, BrandService>();
builder.Services.AddScoped<IItemTypeService, ItemTypeService>();
//builder.Services.AddScoped<ICompanyProfileService, CompanyProfileService>();
builder.Services.AddScoped<IProductConfigService, ProductConfigService>();
builder.Services.AddScoped<IVendorService, VendorService>();
builder.Services.AddScoped<IWarehouseService, WarehouseService>();

// RBAC / sidebar: Role (shadow of AuthHex) + Permission assignment + user management
builder.Services.AddScoped<IRoleService, RoleService>();
builder.Services.AddScoped<IUserManagementService, UserManagementService>();

// Cross-cutting + procurement services (docs/11 §3)
builder.Services.AddScoped<INumberSequenceService, NumberSequenceService>();
builder.Services.AddScoped<IRequisitionService, RequisitionService>();
builder.Services.AddScoped<IRfqService, RfqService>();
builder.Services.AddScoped<IPurchaseOrderService, PurchaseOrderService>();

// Stock core + goods receipt (docs/11 §4–5)
builder.Services.AddScoped<IItemMeasure, ItemMeasure>();
builder.Services.AddScoped<IFifoCostingService, FifoCostingService>();
builder.Services.AddScoped<IStockService, StockService>();
builder.Services.AddScoped<IGrnService, GrnService>();
builder.Services.AddScoped<IGrnPaymentService, GrnPaymentService>();

// Sales (Phase 1)
builder.Services.AddScoped<ISalesPricingService, SalesPricingService>();
builder.Services.AddScoped<ISalesDomainService, SalesDomainService>();
builder.Services.AddScoped<ISalesPostingService, SalesPostingService>();
builder.Services.AddScoped<ISalesDocumentWorkflowService, SalesDocumentWorkflowService>();
builder.Services.AddScoped<ISalesMappingService, SalesMappingService>();
builder.Services.AddScoped<ISalesInvoiceService, SalesInvoiceService>();
builder.Services.AddScoped<ISalesInvoicePaymentService, SalesInvoicePaymentService>();
builder.Services.AddScoped<ISalesSlipService, SalesSlipService>();
builder.Services.AddScoped<IBundleSaleService, BundleSaleService>();
builder.Services.AddScoped<ISalesPromotionSuggestionService, SalesPromotionSuggestionService>();
builder.Services.AddScoped<ISalesReportService, SalesReportService>();
builder.Services.AddScoped<ISalesReturnService, SalesReturnService>();
builder.Services.AddScoped<ISalesDayEndService, SalesDayEndService>();

// Stock transactions + reference data (docs/11 §5–6)
builder.Services.AddScoped<IStockMutator, StockMutator>();
builder.Services.AddScoped<IReasonCodeService, ReasonCodeService>();
builder.Services.AddScoped<IAdjustmentService, AdjustmentService>();
builder.Services.AddScoped<ITransferService, TransferService>();
builder.Services.AddScoped<ICountService, CountService>();
builder.Services.AddScoped<IReorderService, ReorderService>();
builder.Services.AddScoped<IPurchaseReturnService, PurchaseReturnService>();

// Cross-cutting: audit trail + GL-ready journal read access (docs/11 §6 / FR-X-02, FR-STK-13)
builder.Services.AddScoped<IAuditService, AuditService>();

// Dashboard aggregate stats (cross-domain read: stock, GRN, procurement)
builder.Services.AddScoped<IDashboardService, DashboardService>();

// HRM (docs/13-BACKEND-HRM-API.md): org masters, employee core, staff documents
builder.Services.AddSingleton<IFileStorageService, LocalFileStorageService>();
builder.Services.AddScoped<IBranchService, BranchService>();
builder.Services.AddScoped<IDepartmentService, DepartmentService>();
builder.Services.AddScoped<IDesignationService, DesignationService>();
builder.Services.AddScoped<IEmploymentTypeService, EmploymentTypeService>();
builder.Services.AddScoped<IWorkShiftService, WorkShiftService>();
builder.Services.AddScoped<IEmployeeUserLinkService, EmployeeUserLinkService>();
builder.Services.AddScoped<IEmployeeService, EmployeeService>();
builder.Services.AddScoped<IHrDocumentTypeService, HrDocumentTypeService>();
builder.Services.AddScoped<IEmployeeDocumentService, EmployeeDocumentService>();

// HRM: Leave (docs/13-BACKEND-HRM-API.md §5) — LeaveType/LeaveBalance built before
// LeaveRequest since approval increments balances; Attendance depends on LeaveRequest.
builder.Services.AddScoped<ILeaveTypeService, LeaveTypeService>();
builder.Services.AddScoped<ILeaveBalanceService, LeaveBalanceService>();
builder.Services.AddScoped<ILeaveRequestService, LeaveRequestService>();

// HRM: Attendance (docs/13-BACKEND-HRM-API.md §4)
builder.Services.AddScoped<IAttendanceComputationService, AttendanceComputationService>();
builder.Services.AddScoped<IAttendanceUploadService, AttendanceUploadService>();

// HRM: Payroll (docs/13-BACKEND-HRM-API.md §6) — masters/settings before the
// calculation service, which composes them; PayrollRunService orchestrates last.
builder.Services.AddScoped<ISalaryComponentService, SalaryComponentService>();
builder.Services.AddScoped<IEmployeeSalaryStructureService, EmployeeSalaryStructureService>();
builder.Services.AddScoped<IEmployeeLoanService, EmployeeLoanService>();
builder.Services.AddScoped<IPayrollStatutorySettingService, PayrollStatutorySettingService>();
builder.Services.AddScoped<ITaxSlabService, TaxSlabService>();
builder.Services.AddScoped<IPayrollCalculationService, PayrollCalculationService>();
builder.Services.AddScoped<IPayrollRunService, PayrollRunService>();
builder.Services.AddScoped<IPayslipService, PayslipService>();

// HRM: Reports (docs/13-BACKEND-HRM-API.md §6) — read-only, no new entities
builder.Services.AddScoped<IHrReportService, HrReportService>();

// Manufacturing / Production Lines (docs/30-BACKEND-PHASE2.md Part A). Templates stand
// alone; runs consume FifoCostingService for all stock movement. ProductionGraphValidator
// is deliberately unregistered — it is a pure static algorithm, not an injected service.
builder.Services.AddScoped<IProductionTemplateService, ProductionTemplateService>();
builder.Services.AddScoped<IProductionRunService, ProductionRunService>();

// Health checks (EF Core DB)
builder.Services.AddHealthChecks().AddDbContextCheck<ErpDbContext>();

// Swagger / OpenAPI (Swashbuckle v10 → OpenAPI 3.1)
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(o =>
{
    o.SwaggerDoc("v1", new OpenApiInfo { Title = "ERPCore API", Version = "v1" });
    o.CustomSchemaIds(t => t.FullName!.Replace("+", "."));
});

var app = builder.Build();

// Seed configurable reference data (reason codes) idempotently at startup.
using (var scope = app.Services.CreateScope())
{
    var db = scope.ServiceProvider.GetRequiredService<ErpDbContext>();
   // await EnsureMigrationBaselineAsync(db);
    await db.Database.MigrateAsync();
    try
    {
        await DataSeeder.SeedAsync(db);
    }
    catch (Exception ex)
    {
        throw new InvalidOperationException("Database migration succeeded, but startup seeding failed.", ex);
    }
}

app.UseSerilogRequestLogging();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseExceptionHandler();      // -> DomainExceptionHandler / ProblemDetails
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.MapHealthChecks("/health");
app.Run();

