package main

import (
	"bssapp-backend/db"
	"bssapp-backend/internal/auditlog"
	"bssapp-backend/internal/mailer"
	"bssapp-backend/middlewares"
	"bssapp-backend/permissions"
	"bssapp-backend/repository"
	"bssapp-backend/routes"
	"database/sql"
	"log"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"runtime/debug"
	"strings"

	"github.com/gorilla/mux"
	"github.com/joho/godotenv"
)

/*
===========================================================
✅ CORS
===========================================================
*/
func enableCORS(h http.Handler) http.Handler {

	frontendURL := os.Getenv("APP_FRONTEND_URL")

	// Default fallback (dev için)
	if frontendURL == "" {
		frontendURL = "http://localhost:9000"
	}

	log.Println("🌍 CORS Allowed Origin:", frontendURL)

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

		origin := r.Header.Get("Origin")

		// Sadece izin verilen origin'e cevap ver
		if origin == frontendURL {
			w.Header().Set("Access-Control-Allow-Origin", origin)
			w.Header().Set("Vary", "Origin")
			w.Header().Set("Access-Control-Allow-Credentials", "true")
		}

		w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
		w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")

		// Preflight
		if r.Method == http.MethodOptions {
			w.WriteHeader(http.StatusNoContent)
			return
		}

		h.ServeHTTP(w, r)
	})
}

/*
===========================================================
✅ V3 — Method-aware Route Auto Register
- mk_sys_routes: (path, method, module_code, action)
- unique: (path, method)
- admin auto-allow: mk_sys_role_permissions (role_id=3)
===========================================================
*/
func autoRegisterRouteV3(
	pg *sql.DB,
	path string,
	method string,
	module string,
	action string,
) {
	tx, err := pg.Begin()
	if err != nil {
		log.Println("❌ TX begin error:", err)
		return
	}
	defer tx.Rollback()

	// 1) ROUTE REGISTER (path+method)
	_, err = tx.Exec(`
		INSERT INTO mk_sys_routes
			(path, method, module_code, action)
		VALUES
			($1, $2, $3, $4)
		ON CONFLICT (path, method) DO UPDATE
		SET
			module_code = EXCLUDED.module_code,
			action      = EXCLUDED.action
	`,
		path,
		method,
		module,
		action,
	)
	if err != nil {
		log.Printf("❌ Route register error (%s %s): %v", method, path, err)
		return
	}

	// 2) ADMIN AUTO PERMISSION (module+action bazlı)
	_, err = tx.Exec(`
		INSERT INTO mk_sys_role_permissions
			(role_id, module_code, action, allowed)
		SELECT
			id,
			$1,
			$2,
			true
		FROM dfrole
		WHERE id = 3 -- ADMIN
		ON CONFLICT DO NOTHING
	`,
		module,
		action,
	)
	if err != nil {
		log.Printf("❌ Admin perm seed error (%s %s): %v", method, path, err)
		return
	}

	if err := tx.Commit(); err != nil {
		log.Println("❌ TX commit error:", err)
		return
	}

	log.Printf("✅ Route+Perm registered → %s %s [%s:%s]",
		method, path, module, action,
	)
}

/*
===========================================================
✅ V3 Route Bind Helper
- tek satırda: autoRegister + wrap + Handle
===========================================================
*/
func bindV3(
	r *mux.Router,
	pg *sql.DB,
	path string,
	method string,
	module string,
	action string,
	h http.Handler,
) {
	// main
	autoRegisterRouteV3(pg, path, method, module, action)

	r.Handle(path, h).Methods(method, "OPTIONS")
}

/*
===========================================================
InitRoutes — FULL V3 (Method-aware) PERMISSION EDITION
===========================================================
*/
func InitRoutes(pgDB *sql.DB, mssql *sql.DB, ml *mailer.GraphMailer) *mux.Router {

	r := mux.NewRouter()
	mountSPA(r)

	/*
		===========================================================
		✅ wrapV3 (method-aware):
		- AuthMiddleware (JWT)
		- panic recover
		- AuthzGuardByRoute(pgDB) => route(path+method) lookup
		===========================================================
	*/
	wrapV3 := func(h http.Handler) http.Handler {
		return middlewares.AuthMiddleware(
			pgDB,
			http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
				defer func() {
					if rec := recover(); rec != nil {
						log.Printf("🔥 PANIC %s %s\n%v", r.Method, r.URL.Path, rec)
						debug.PrintStack()
						http.Error(w, "internal server error", 500)
					}
				}()

				// ✅ method-aware route guard
				middlewares.AuthzGuardByRoute(pgDB)(h).ServeHTTP(w, r)
			}),
		)
	}

	// ============================================================
	// PUBLIC (NO AUTHZ)
	// ============================================================
	bindV3(r, pgDB,
		"/api/auth/login", "POST",
		"auth", "login",
		http.HandlerFunc(routes.LoginHandler(pgDB)),
	)

	bindV3(r, pgDB,
		"/api/auth/refresh", "POST",
		"auth", "refresh",
		routes.AuthRefreshHandler(pgDB),
	)

	// ============================================================
	// SYSTEM
	// ============================================================
	bindV3(r, pgDB,
		"/api/password/change", "POST",
		"system", "update",
		wrapV3(http.HandlerFunc(routes.FirstPasswordChangeHandler(pgDB))),
	)

	bindV3(r, pgDB,
		"/api/activity-logs", "GET",
		"user", "view",
		wrapV3(routes.AdminActivityLogsHandler(pgDB)),
	)

	bindV3(r, pgDB,
		"/api/test-mail", "POST",
		"user", "insert",
		wrapV3(routes.TestMailHandler(ml)),
	)

	// ============================================================
	// PERMISSIONS
	// ============================================================
	rolePerm := "/api/roles/{id}/permissions"

	bindV3(r, pgDB,
		rolePerm, "GET",
		"user", "update",
		wrapV3(routes.GetRolePermissionMatrix(pgDB)),
	)
	bindV3(r, pgDB,
		rolePerm, "POST",
		"user", "update",
		wrapV3(routes.SaveRolePermissionMatrix(pgDB)),
	)

	userPerm := "/api/users/{id}/permissions"

	bindV3(r, pgDB,
		userPerm, "GET",
		"user", "update",
		wrapV3(routes.GetUserPermissionsHandler(pgDB)),
	)
	bindV3(r, pgDB,
		userPerm, "POST",
		"user", "update",
		wrapV3(routes.SaveUserPermissionsHandler(pgDB)),
	)

	// ✅ permissions/routes (system:view)
	bindV3(r, pgDB,
		"/api/permissions/routes", "GET",
		"system", "view",
		wrapV3(routes.GetUserRoutePermissionsHandler(pgDB)),
	)

	// ✅ permissions/effective (system:view)
	bindV3(r, pgDB,
		"/api/permissions/effective", "GET",
		"system", "view",
		wrapV3(routes.GetMyEffectivePermissions(pgDB)),
	)

	// ✅ my permission matrix (system:view)  (Senin tabloda user:view görünüyor; düzeltiyoruz)
	bindV3(r, pgDB,
		"/api/permissions/matrix", "GET",
		"system", "view",
		wrapV3(routes.GetMyPermissionMatrix(pgDB)),
	)

	// ============================================================
	// ROLE + DEPARTMENT PERMISSIONS
	// ============================================================
	rdPerm := "/api/roles/{roleId}/departments/{deptCode}/permissions"
	rdHandler := routes.NewRoleDepartmentPermissionHandler(pgDB)

	bindV3(r, pgDB,
		"/api/role-dept-permissions/list", "GET",
		"user", "update",
		wrapV3(http.HandlerFunc(rdHandler.List)),
	)
	bindV3(r, pgDB,
		rdPerm, "GET",
		"user", "update",
		wrapV3(http.HandlerFunc(rdHandler.Get)),
	)
	bindV3(r, pgDB,
		rdPerm, "POST",
		"user", "update",
		wrapV3(http.HandlerFunc(rdHandler.Save)),
	)

	// ============================================================
	// USERS
	// ============================================================
	bindV3(r, pgDB,
		"/api/users/list", "GET",
		"user", "view",
		wrapV3(routes.UserListRoute(pgDB)),
	)

	bindV3(r, pgDB,
		"/api/users", "POST",
		"user", "insert",
		wrapV3(routes.UserCreateRoute(pgDB)),
	)

	bindV3(r, pgDB,
		"/api/users/{id}", "GET",
		"user", "update",
		wrapV3(routes.UserDetailRoute(pgDB)),
	)
	bindV3(r, pgDB,
		"/api/users/{id}", "PUT",
		"user", "update",
		wrapV3(routes.UserDetailRoute(pgDB)),
	)
	bindV3(r, pgDB,
		"/api/users/{id}", "DELETE",
		"user", "delete",
		wrapV3(routes.UserDetailRoute(pgDB)),
	)

	bindV3(r, pgDB,
		"/api/users/{id}/admin-reset-password", "POST",
		"user", "update",
		wrapV3(routes.AdminResetPasswordHandler(pgDB)),
	)

	bindV3(r, pgDB,
		"/api/users/{id}/send-password-mail", "POST",
		"user", "update",
		wrapV3(routes.SendPasswordResetMailHandler(pgDB, ml)),
	)

	// ✅ eski kısayol create endpoint (senin eski main.go’da vardı)
	bindV3(r, pgDB,
		"/api/users/create", "POST",
		"user", "insert",
		wrapV3(routes.UserCreateRoute(pgDB)),
	)

	// ============================================================
	// LOOKUPS
	// ============================================================
	lookups := map[string]http.Handler{
		"/api/lookups/roles":            routes.GetRoleLookupRoute(pgDB),
		"/api/lookups/departments":      routes.GetDepartmentLookupRoute(pgDB),
		"/api/lookups/nebim-users":      routes.GetNebimUserLookupRoute(pgDB),
		"/api/lookups/piyasalar":        routes.GetPiyasaLookupRoute(pgDB),
		"/api/lookups/users-perm":       routes.GetUsersForPermissionSelectRoute(pgDB),
		"/api/lookups/roles-perm":       routes.GetRolesForPermissionSelectRoute(pgDB),
		"/api/lookups/departments-perm": routes.GetDepartmentsForPermissionSelectRoute(pgDB),
		"/api/lookups/modules":          routes.GetModuleLookupRoute(pgDB),
	}

	for path, handler := range lookups {
		bindV3(r, pgDB,
			path, "GET",
			"user", "view",
			wrapV3(handler),
		)
	}

	// ============================================================
	// CUSTOMER
	// ============================================================
	bindV3(r, pgDB,
		"/api/accounts", "GET",
		"customer", "view",
		wrapV3(http.HandlerFunc(routes.GetAccountsHandler)),
	)

	bindV3(r, pgDB,
		"/api/customer-list", "GET",
		"customer", "view",
		wrapV3(http.HandlerFunc(routes.GetCustomerListHandler)),
	)

	// ============================================================
	// FINANCE
	// ============================================================
	bindV3(r, pgDB,
		"/api/today-currency", "GET",
		"finance", "view",
		wrapV3(routes.GetTodayCurrencyV3Handler(mssql)),
	)

	bindV3(r, pgDB,
		"/api/export-pdf", "GET",
		"finance", "export",
		wrapV3(routes.ExportPDFHandler(mssql)),
	)

	bindV3(r, pgDB,
		"/api/exportstamentheaderreport-pdf", "GET",
		"finance", "export",
		wrapV3(routes.ExportStatementHeaderReportPDFHandler(mssql)),
	)

	// ============================================================
	// REPORT (STATEMENTS)
	// ============================================================
	bindV3(r, pgDB,
		"/api/statements", "GET",
		"finance", "view",
		wrapV3(http.HandlerFunc(routes.GetStatementHeadersHandler)),
	)

	// ⚠️ Senin handler: GetStatementDetailsHandler vars["accountCode"] bekliyor,
	// route’da {id} var. Burada, DB route’unu ve path’i bozmayalım diye {id}’yi koruyorum,
	// ama handler içinde accountCode := mux.Vars(r)["id"] yapman daha doğru.
	bindV3(r, pgDB,
		"/api/statements/{id}/details", "GET",
		"finance", "view",
		wrapV3(http.HandlerFunc(routes.GetStatementDetailsHandler)),
	)

	// ============================================================
	// ORDER
	// ============================================================
	orderRoutes := []struct {
		Path   string
		Method string
		Action string
		Handle http.Handler
	}{
		{"/api/order/create", "POST", "insert", routes.CreateOrderHandler(pgDB, mssql)},
		{"/api/order/update", "POST", "update", http.HandlerFunc(routes.UpdateOrderHandler)},
		{"/api/order/get/{id}", "GET", "view", routes.GetOrderByIDHandler(mssql)},
		{"/api/orders/list", "GET", "view", routes.OrderListRoute(mssql)},
		{"/api/orders/close-ready", "GET", "update", routes.OrderCloseReadyListRoute(mssql)},
		{"/api/orders/bulk-close", "POST", "update", routes.OrderBulkCloseRoute(mssql)},
		{"/api/orders/export", "GET", "export", routes.OrderListExcelRoute(mssql)},
		{"/api/order/check/{id}", "GET", "view", routes.OrderExistsHandler(mssql)},
		{"/api/order/validate", "POST", "insert", routes.ValidateOrderHandler(mssql)},
		{"/api/order/pdf/{id}", "GET", "export", routes.OrderPDFHandler(mssql)},
		{"/api/order-inventory", "GET", "view", http.HandlerFunc(routes.GetOrderInventoryHandler)},
		{"/api/orderpricelistb2b", "GET", "view", routes.GetOrderPriceListB2BHandler(pgDB, mssql)},
		{"/api/min-price", "GET", "view", routes.GetOrderPriceListB2BHandler(pgDB, mssql)},
	}

	for _, rt := range orderRoutes {
		bindV3(r, pgDB,
			rt.Path, rt.Method,
			"order", rt.Action,
			wrapV3(rt.Handle),
		)
	}

	// ============================================================
	// PRODUCTS (✅ handler mapping fix)
	// ============================================================
	bindV3(r, pgDB,
		"/api/products", "GET",
		"order", "view",
		wrapV3(http.HandlerFunc(routes.GetProductListHandler)),
	)

	bindV3(r, pgDB,
		"/api/product-detail", "GET",
		"order", "view",
		wrapV3(http.HandlerFunc(routes.GetProductDetailHandler)),
	)

	bindV3(r, pgDB,
		"/api/product-colors", "GET",
		"order", "view",
		wrapV3(http.HandlerFunc(routes.GetProductColorsHandler)),
	)

	bindV3(r, pgDB,
		"/api/product-colorsize", "GET",
		"order", "view",
		wrapV3(http.HandlerFunc(routes.GetProductColorSizesHandler)),
	)

	bindV3(r, pgDB,
		"/api/product-secondcolor", "GET",
		"order", "view",
		wrapV3(http.HandlerFunc(routes.GetProductSecondColorsHandler)),
	)

	// ============================================================
	// ROLE MANAGEMENT
	// ============================================================
	bindV3(r, pgDB,
		"/api/roles", "GET",
		"user", "view",
		wrapV3(routes.GetRolesHandler(pgDB)),
	)

	bindV3(r, pgDB,
		"/api/departments", "GET",
		"user", "view",
		wrapV3(routes.GetDepartmentsHandler(pgDB)),
	)

	bindV3(r, pgDB,
		"/api/piyasalar", "GET",
		"user", "view",
		wrapV3(routes.GetPiyasalarHandler(pgDB)),
	)

	// ============================================================
	// ROLE RELATIONS
	// ============================================================
	bindV3(r, pgDB,
		"/api/roles/{id}/departments", "POST",
		"user", "update",
		wrapV3(routes.UpdateRoleDepartmentsHandler(pgDB)),
	)

	bindV3(r, pgDB,
		"/api/roles/{id}/piyasalar", "POST",
		"user", "update",
		wrapV3(routes.UpdateRolePiyasalarHandler(pgDB)),
	)

	// ============================================================
	// USER ↔ ROLE
	// ============================================================
	bindV3(r, pgDB,
		"/api/users/{id}/roles", "POST",
		"user", "update",
		wrapV3(routes.UpdateUserRolesHandler(pgDB)),
	)

	// ============================================================
	// ADMIN EXTRA (eski main.go’da vardı, yeni sisteme alındı)
	// ============================================================
	bindV3(r, pgDB,
		"/api/admin/users/{id}/piyasa-sync", "POST",
		"admin", "user.update",
		wrapV3(http.HandlerFunc(routes.AdminSyncUserPiyasaHandler)),
	)

	return r
}

func main() {
	log.Println("🔥🔥🔥 BSSAPP BACKEND STARTED — LOGIN ROUTE SHOULD EXIST 🔥🔥🔥")

	// -------------------------------------------------------
	// 🔑 ENV
	// -------------------------------------------------------
	if err := godotenv.Load(".env", "mail.env"); err != nil {
		log.Println("⚠️ .env / mail.env bulunamadı")
	}

	jwtSecret := os.Getenv("JWT_SECRET")
	if len(jwtSecret) < 10 {
		log.Fatal("❌ JWT_SECRET tanımlı değil veya çok kısa (min 10 karakter)")
	}
	log.Println("🔐 JWT_SECRET yüklendi")

	// -------------------------------------------------------
	// 🔗 DATABASE
	// -------------------------------------------------------
	db.ConnectMSSQL()

	pgDB, err := db.ConnectPostgres()
	if err != nil {
		log.Fatalf("❌ PostgreSQL bağlantı hatası: %v", err)
	}
	defer pgDB.Close()

	// -------------------------------------------------------
	// 🔐 ADMIN ROLE + DEPARTMENT AUTO SEED
	// -------------------------------------------------------
	if err := permissions.SeedAdminRoleDepartments(pgDB); err != nil {
		log.Println("❌ Admin dept seed failed:", err)
	} else {
		log.Println("✅ Admin dept permissions seeded")
	}

	// -------------------------------------------------------
	// 🕵️ AUDIT LOG INIT
	// -------------------------------------------------------
	auditlog.Init(pgDB, 1000)
	log.Println("🕵️ AuditLog sistemi başlatıldı (buffer=1000)")

	// -------------------------------------------------------
	// ✉️ MAILER INIT
	// -------------------------------------------------------
	graphMailer, err := mailer.NewGraphMailer()
	if err != nil {
		log.Fatalf("❌ Graph Mailer init error: %v", err)
	}
	log.Println("✉️ Graph Mailer hazır")

	// -------------------------------------------------------
	// 👤 DEBUG
	// -------------------------------------------------------
	repository.NewUserRepository(pgDB).DebugListUsers()

	// -------------------------------------------------------
	// 🌍 SERVER
	// -------------------------------------------------------
	router := InitRoutes(pgDB, db.MssqlDB, graphMailer)

	handler := enableCORS(
		middlewares.GlobalAuthMiddleware(
			pgDB,
			middlewares.RequestLogger(router),
		),
	)

	log.Println("✅ Server çalışıyor: http://localhost:8080")
	log.Fatal(http.ListenAndServe(":8080", handler))
}

func mountSPA(r *mux.Router) {
	r.NotFoundHandler = http.HandlerFunc(spaIndex)
	r.HandleFunc("/", spaIndex).Methods(http.MethodGet)
}

func spaIndex(w http.ResponseWriter, r *http.Request) {
	uiDir := uiRootDir()
	p := r.URL.Path

	if r.URL.Path == "/logo.png" {
		_, err := os.Stat("./logo.png")
		if err == nil {
			http.ServeFile(w, r, "./logo.png")
			return
		}
	}

	if !strings.HasPrefix(p, "/") {
		p = "/" + p
		r.URL.Path = p
	}
	p = path.Clean(p)
	if p == "/" {
		p = "index.html"
	}

	if strings.HasPrefix(p, "/api") {
		http.NotFound(w, r)
		return
	}

	name := path.Join(uiDir, filepath.FromSlash(p))

	f, err := os.Stat(name)
	if err != nil {
		if os.IsNotExist(err) {
			http.ServeFile(w, r, filepath.Join(uiDir, "index.html"))
			return
		}

		http.Error(w, "internal server error", http.StatusInternalServerError)
		return
	}

	if f.IsDir() {
		http.Error(w, "forbidden", http.StatusForbidden)
		return
	}

	http.ServeFile(w, r, name)
}

func uiRootDir() string {
	if d := strings.TrimSpace(os.Getenv("UI_DIR")); d != "" {
		return d
	}

	candidates := []string{"../ui/dist", "./ui/dist"}
	for _, d := range candidates {
		if fi, err := os.Stat(d); err == nil && fi.IsDir() {
			return d
		}
	}

	return "../ui/dist"
}
