package http_router

import (
	"fmt"
	"log/slog"
	"net/http"
	"net/http/pprof"
	"strings"

	"git-molva.ru/Molva/molva-backend/services/api_gateway/internal/logger"

	"git-molva.ru/Molva/molva-backend/services/api_gateway/internal/cache"
	"git-molva.ru/Molva/molva-backend/services/api_gateway/internal/database"
	"git-molva.ru/Molva/molva-backend/services/api_gateway/internal/feed"
	"git-molva.ru/Molva/molva-backend/services/api_gateway/internal/file_manager/s3_storage"
	"git-molva.ru/Molva/molva-backend/services/api_gateway/internal/integration"
	objectStorage "git-molva.ru/Molva/molva-backend/services/api_gateway/internal/object_storage"
	"git-molva.ru/Molva/molva-backend/services/api_gateway/internal/services/agent"
	authinfra "git-molva.ru/Molva/molva-backend/services/api_gateway/internal/services/auth_infrastructure"
	"git-molva.ru/Molva/molva-backend/services/api_gateway/internal/services/distributor"

	pgdb "git-molva.ru/Molva/molva-backend/services/api_gateway/internal/database/postgres"
	formgenerator "git-molva.ru/Molva/molva-backend/services/api_gateway/internal/form_generator"

	"git-molva.ru/Molva/molva-backend/services/api_gateway/internal/config"
	"github.com/gorilla/mux"
)

type Router struct {
	Mux                *mux.Router
	env                string
	logger             *slog.Logger
	middleware         *Middleware
	shortenerCfg       *config.SecretConfig
	abstractAPI        string
	cache              cache.Client
	objectStorage      objectStorage.Client
	feedHandler        *feed.Handler
	integrationClient  integration.Client
	databaseClient     database.Client
	agentService       agent.AgentService
	distributorService distributor.DistributorService
}

func New(logger *slog.Logger, cfg *config.Config) (*Router, error) {
	middleware, err := NewMiddleware(&ConfigMiddleware{
		logger:      logger,
		keycloakCfg: &cfg.Keycloak,
	})
	if err != nil {
		return nil, err
	}

	cacheClient, err := cache.New(cache.ValKeyCacheConfig{
		Addrs:          strings.Split(cfg.ValkeyCache.Addrs, ","),
		Password:       cfg.ValkeyCache.Password,
		ReadOnly:       cfg.ValkeyCache.ReadOnly,
		DialTimeout:    cfg.ValkeyCache.DialTimeout,
		PoolSize:       cfg.ValkeyCache.PoolSize,
		DefaultTTL:     cfg.ValkeyCache.DefaultTTL,
		RootCaFilePath: cfg.ValkeyCache.RootCaFilePath,
	}, cache.ValkeyCacheType)
	if err != nil {
		return nil, err
	}

	databaseClient, err := database.New(database.PostgresClientType, pgdb.PostgresConfig{
		Host:        cfg.Database.Host,
		Port:        cfg.Database.Port,
		Username:    cfg.Database.Username,
		Password:    cfg.Database.Password,
		Database:    cfg.Database.DBName,
		Schema:      cfg.Database.Schema,
		SSLMode:     cfg.Database.SSLMode,
		SSLRootCert: cfg.Database.RootCaFilePath,
	})
	if err != nil {
		return nil, err
	}

	agentService, err := agent.New(agent.CrmAgentServiceType, agent.CrmAgentServiceConfig{
		DbClient: databaseClient,
	})
	if err != nil {
		return nil, err
	}

	distributorService, err := distributor.New(distributor.CrmDistributorServiceType, distributor.CrmDistributorServiceConfig{
		DbClient: databaseClient,
	})
	if err != nil {
		return nil, err
	}

	objectStorageClient, err := objectStorage.New(objectStorage.S3StorageConfig{
		Bucket:         cfg.S3Storage.Bucket,
		DefaultLinkTTL: cfg.ValkeyCache.DefaultTTL,
	}, objectStorage.ClientTypeS3)
	if err != nil {
		return nil, err
	}

	integrationClient, err := integration.New(integration.Config{
		Cache:  cacheClient,
		Db:     databaseClient,
		Logger: logger,
		Secrets: map[string]integration.CompanySecrets{
			integration.VkusvillCompanyName: integration.VkusvillSecretsConfig{
				ApiToken: cfg.Integration.Vkusvill.ApiToken,
			},
		},
	})
	if err != nil {
		return nil, err
	}

	// TODO: вырезать ненужные аргументы в рамках https://tracker.yandex.ru/MOLVARAZRABOTKA-363
	service, err := feed.NewService(fmt.Sprintf(
		"postgres://%s:%s@%s:%d/%s?sslmode=%s",
		cfg.Database.Username,
		cfg.Database.Password,
		cfg.Database.Host,
		cfg.Database.Port,
		cfg.Database.DBName,
		cfg.Database.SSLMode,
	), cfg.Database.Schema, logger, databaseClient)
	if err != nil {
		return nil, err
	}

	return &Router{
		Mux:                mux.NewRouter(),
		env:                cfg.Env,
		logger:             logger,
		middleware:         middleware,
		shortenerCfg:       &cfg.Secret,
		abstractAPI:        cfg.EmailVerificationService.APIKey, // may be nil dereference if no email verification service is configured
		cache:              cacheClient,
		objectStorage:      objectStorageClient,
		feedHandler:        feed.NewFeedHandler(logger, service),
		databaseClient:     databaseClient,
		agentService:       agentService,
		distributorService: distributorService,
		integrationClient:  integrationClient,
	}, nil
}

func SetupRouter(r *Router, buildCfg config.BuildInfo) {
	authInfraService, err := authinfra.New(authinfra.CacheAuthInfrastructureServiceType, authinfra.CacheAuthInfraServiceConfig{
		CacheClient: r.cache,
	})
	if err != nil {
		panic(err)
	}

	s3FileManager := s3_storage.NewS3Storage(
		r.objectStorage,
		r.cache,
	)

	h := newHandler(&Config{
		env:                            r.env,
		logger:                         r.logger,
		authManager:                    r.middleware.authManager,
		authInfraService:               authInfraService,
		fileManager:                    s3FileManager,
		dbClient:                       r.databaseClient,
		agentService:                   r.agentService,
		distributorService:             r.distributorService,
		secretConfig:                   r.shortenerCfg,
		emailVerificationServiceAPIKey: r.abstractAPI,
		cacheClient:                    r.cache,
		objectStorageClient:            r.objectStorage,
		feed:                           r.feedHandler,
		integrationClient:              r.integrationClient,
		buildConfig:                    buildCfg,
	})

	r.Mux.Use(r.middleware.loggingMiddleware)

	setupAuthHandlers(r, h)
	setupIntegrationHandlers(r, h)
	setupClientHandlers(r, h)
	setupValidationHandlers(r, h)

	if r.env != logger.EnvProd {
		setupSystemInfoHandlers(r, h)
	}

	subRouter := r.Mux.PathPrefix("/api/v1").Subrouter()
	subRouter.Use(r.middleware.authMiddleware)

	setupAgentHandlers(subRouter, h)
	setupDistributorHandlers(subRouter, h)
	setupEmployeesHandlers(subRouter, h)
	setupLogoHandlers(subRouter, h)

	// --------------- DOCUMENTS FOR FORM ------------
	r.Mux.HandleFunc("/api/v1/docs/{file}", h.getFileHandler).Methods(http.MethodGet)

	// --------------- FEED ------------
	subRouter.HandleFunc("/feed/{uid}/events", h.GetUserEventsHandler).Methods(http.MethodGet)
}

func setupAuthHandlers(r *Router, h *handler) {
	r.Mux.HandleFunc("/api/v1/login", h.loginHandler).Methods(http.MethodPost)
	r.Mux.HandleFunc("/api/v1/register", h.registerHandler).Methods(http.MethodPost)
	r.Mux.HandleFunc("/api/v1/logout", h.logoutHandler).Methods(http.MethodPost)
	r.Mux.HandleFunc("/api/v1/refresh-token", h.refreshTokenHandler).Methods(http.MethodPost)

	r.Mux.HandleFunc("/api/v1/verify_email", h.verifyEmailHandler).Methods(http.MethodPatch)
	r.Mux.HandleFunc("/api/v1/verify_email", h.getEmailVerificationStatusHandler).Methods(http.MethodGet)
	r.Mux.HandleFunc("/api/v1/confirm_email", h.confirmEmailPageHandler).Methods(http.MethodGet)

	r.Mux.HandleFunc("/api/v1/forgot_password", h.forgotPasswordHandler).Methods(http.MethodPost)
	r.Mux.HandleFunc("/api/v1/validate_otp", h.validateOTPHandler).Methods(http.MethodGet)
	r.Mux.HandleFunc("/api/v1/reset_password", h.resetPasswordHandler).Methods(http.MethodPut)
}

func setupAgentHandlers(subRouter *mux.Router, h *handler) {
	// // =============== COMPANY HANDLERS ===============
	subRouter.HandleFunc("/agents/{agent_id}/companies", h.getCompanyListAgentHandler).Methods(http.MethodGet)
	subRouter.HandleFunc("/agents/{agent_id}/company/{company_id}", h.getCompanyByIdAgentHandler).Methods(http.MethodGet)
	// TODO: review
	// subRouter.HandleFunc("/agents/{agent_id}/company/{company_id}", h.addNewAgentCompanyMemberHandler).Methods(http.MethodPost)
	subRouter.HandleFunc("/agents/{agent_id}/company", h.createCompanyAgentHandler).Methods(http.MethodPost)
	subRouter.HandleFunc("/agents/{agent_id}/company/{company_id}", h.updateCompanyAgentHandler).Methods(http.MethodPatch)

	// // =============== VACANCY HANDLERS ===============
	subRouter.HandleFunc("/agents/{agent_id}/vacancies", h.getVacancyListAgentHandler).Methods(http.MethodGet)
	subRouter.HandleFunc("/agents/{agent_id}/vacancies/{vacancy_id}", h.getPersonalLinkHandler).Methods(http.MethodGet)

	// // =============== SUBMISSION HANDLERS ===============
	subRouter.HandleFunc("/agents/{agent_id}/submissions", h.getSubmissionListAgentHandler).Methods(http.MethodGet)
	subRouter.HandleFunc("/agents/{agent_id}/submissions/{submission_id}", h.deleteSubmissionAgentHandler).Methods(http.MethodDelete)
	subRouter.HandleFunc("/agents/{agent_id}/submissions/{submission_id}/cv", h.getSubmissionCVHandler).Methods(http.MethodGet)

	// // =============== PROFILE HANDLERS ===============
	subRouter.HandleFunc("/agents/{agent_id}/profile", h.getProfileAgentHandler).Methods(http.MethodGet)
	subRouter.HandleFunc("/agents/{agent_id}/profile", h.updateProfileAgentHandler).Methods(http.MethodPatch)

	// // =============== BALANCE HANDLERS ===============
	subRouter.HandleFunc("/agents/{agent_id}/balance", h.getBalanceAgentHandler).Methods(http.MethodGet)
	subRouter.HandleFunc("/agents/{agent_id}/transactions", h.getTransactionListAgentHandler).Methods(http.MethodGet)
	subRouter.HandleFunc("/agents/{agent_id}/transactions", h.createTransactionAgentHandler).Methods(http.MethodPost)
	subRouter.HandleFunc("/agents/{agent_id}/bank_accounts", h.getBankAccountListAgentHandler).Methods(http.MethodGet)
	subRouter.HandleFunc("/agents/{agent_id}/bank_accounts", h.createBankAccountAgentHandler).Methods(http.MethodPost)
	subRouter.HandleFunc("/agents/{agent_id}/bank_accounts/{bank_account_id}", h.updateBankAccountAgentHandler).Methods(http.MethodPatch)
	subRouter.HandleFunc("/agents/{agent_id}/bank_accounts/{bank_account_id}", h.deleteBankAccountAgentHandler).Methods(http.MethodDelete)
}

func setupDistributorHandlers(subRouter *mux.Router, h *handler) {
	// // =============== COMPANY HANDLERS ===============
	subRouter.HandleFunc("/distributor/{distributor_id}/companies", h.getCompanyListDistributorHandler).Methods(http.MethodGet)
	subRouter.HandleFunc("/distributor/{distributor_id}/company/{company_id}", h.getCompanyByIdDistributorHandler).Methods(http.MethodGet)
	subRouter.HandleFunc("/distributor/{distributor_id}/company/{company_id}", h.addCompanyMemberDistributorHandler).Methods(http.MethodPost)
	subRouter.HandleFunc("/distributor/{distributor_id}/company/{company_id}", h.updateCompanyDistributorHandler).Methods(http.MethodPatch)
	subRouter.HandleFunc("/distributor/{distributor_id}/company", h.createCompanyDistributorHandler).Methods(http.MethodPost)

	// // =============== VACANCY HANDLERS ===============
	subRouter.HandleFunc("/distributor/{distributor_id}/vacancies", h.getVacancyListDistributorHandler).Methods(http.MethodGet)
	subRouter.HandleFunc("/distributor/{distributor_id}/vacancies", h.createVacancyDistributorHandler).Methods(http.MethodPost)
	subRouter.HandleFunc("/distributor/{distributor_id}/vacancies/{vacancy_id}", h.updateVacancyDistributorHandler).Methods(http.MethodPatch)
	subRouter.HandleFunc("/distributor/{distributor_id}/vacancies/{vacancy_id}", h.deleteVacancyDistributorHandler).Methods(http.MethodDelete)
	subRouter.HandleFunc("/distributor/{distributor_id}/vacancies/{vacancy_id}/moderation", h.sendVacancyToModerationHandler).Methods(http.MethodPost)

	// // =============== SUBMISSION HANDLERS ===============
	subRouter.HandleFunc("/distributor/{distributor_id}/vacancies/{vacancy_id}/submissions", h.getSubmissionListDistributorHandler).Methods(http.MethodGet)

	subRouter.HandleFunc("/distributor/{distributor_id}/vacancies/{vacancy_id}/submissions/{submission_id}/status", h.updateSubmissionStatusDistributorHandler).Methods(http.MethodPost)
	subRouter.HandleFunc("/distributor/{distributor_id}/submissions/{submission_id}/cv", h.getSubmissionCVHandler).Methods(http.MethodGet)

	// // =============== PROFILE HANDLERS ===============
	subRouter.HandleFunc("/distributor/{distributor_id}/profile", h.getProfileDisributorHandler).Methods(http.MethodGet)
	subRouter.HandleFunc("/distributor/{distributor_id}/profile", h.updateProfileDistributorHandler).Methods(http.MethodPatch)

	// // =============== BALANCE HANDLERS ===============
	subRouter.HandleFunc("/distributor/{distributor_id}/balance", h.getBalanceDistributorHandler).Methods(http.MethodGet)
	subRouter.HandleFunc("/distributor/{distributor_id}/company/{company_id}/balance", h.getCompanyBalanceDistributorHandler).Methods(http.MethodGet)
	subRouter.HandleFunc("/distributor/{distributor_id}/transactions", h.getTransactionListDistributorHandler).Methods(http.MethodGet)
	subRouter.HandleFunc("/distributor/{distributor_id}/company/{company_id}/transactions", h.getCompanyTransactionListDistributorHandler).Methods(http.MethodGet)
	subRouter.HandleFunc("/distributor/{distributor_id}/transactions", h.createTransactionDistributorHandler).Methods(http.MethodPost)
	subRouter.HandleFunc("/distributor/{distributor_id}/bank_accounts", h.getBankAccountListDistributorHandler).Methods(http.MethodGet)
	subRouter.HandleFunc("/distributor/{distributor_id}/company/{company_id}/bank_accounts", h.getCompanyBankAccountListDistributorHandler).Methods(http.MethodGet)
	subRouter.HandleFunc("/distributor/{distributor_id}/company/{company_id}/bank_accounts", h.createBankAccountDistributorHandler).Methods(http.MethodPost)
	subRouter.HandleFunc("/distributor/{distributor_id}/bank_accounts/{bank_account_id}", h.updateBankAccountDistributorHandler).Methods(http.MethodPut)
	subRouter.HandleFunc("/distributor/{distributor_id}/bank_accounts/{bank_account_id}", h.deleteBankAccountDistributorHandler).Methods(http.MethodDelete)
}

func setupEmployeesHandlers(subRouter *mux.Router, h *handler) {
	// // =============== EMPLOYEES HANDLERS ===============
	subRouter.HandleFunc("/companies/{company_id}/employees", h.getEmployeesHandler).Methods(http.MethodGet)
}

func setupClientHandlers(r *Router, h *handler) {
	// // =============== CLIENT HANDLERS ===============
	r.Mux.HandleFunc("/api/v1/anketa", h.getAnketaHandler).Methods(http.MethodGet)
	r.Mux.HandleFunc("/api/v1/anketa", h.postAnketaHandler).Methods(http.MethodPost)

	// --------------- FORM METADATA ---------------
	//TODO FROM ALEXANDER: make err validation
	formSys, _ := formgenerator.GetFileSystem()

	r.Mux.PathPrefix("/api/v1/anketa/static/").Handler(http.StripPrefix("/api/v1/anketa/static/", http.FileServer(http.FS(formSys))))
}

func setupValidationHandlers(r *Router, h *handler) {
	// // =============== VALIDATION HANDLERS ===============
	r.Mux.HandleFunc("/api/v1/{uid}/validation", h.getUserValidationStatusHandler).Methods(http.MethodGet)
}

func setupLogoHandlers(subRouter *mux.Router, h *handler) {
	// // =============== LOGO HANDLERS ===============
	subRouter.HandleFunc("/distributor/{distributor_id}/company/{company_id}/logo", h.getCompanyLogoHandler).Methods(http.MethodGet)
	subRouter.HandleFunc("/distributor/{distributor_id}/company/{company_id}/logo", h.createCompanyLogoHandler).Methods(http.MethodPost)
	subRouter.HandleFunc("/distributor/{distributor_id}/company/{company_id}/logo", h.updateCompanyLogoHandler).Methods(http.MethodPut)
	subRouter.HandleFunc("/distributor/{distributor_id}/company/{company_id}/logo", h.deleteCompanyLogoHandler).Methods(http.MethodDelete)
}

func setupIntegrationHandlers(r *Router, h *handler) {
	r.Mux.HandleFunc("/api/v1/integration/vkusvill/callback", h.vkusvillIntegrationCallbackHandler).Methods(http.MethodPost)
}

func setupSystemInfoHandlers(r *Router, h *handler) {
	r.Mux.HandleFunc("/api/v1/healthcheck", h.getBuildInfoHandler).Methods(http.MethodGet)
	r.Mux.HandleFunc("/debug/pprof/", pprof.Index).Methods(http.MethodGet)
	r.Mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline).Methods(http.MethodGet)
	r.Mux.HandleFunc("/debug/pprof/profile", pprof.Profile).Methods(http.MethodGet)
	r.Mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol).Methods(http.MethodGet)
	r.Mux.HandleFunc("/debug/pprof/trace", pprof.Trace).Methods(http.MethodGet)
	r.Mux.Handle("/debug/pprof/goroutine", pprof.Handler("goroutine")).Methods(http.MethodGet)
	r.Mux.Handle("/debug/pprof/heap", pprof.Handler("heap")).Methods(http.MethodGet)
	r.Mux.Handle("/debug/pprof/threadcreate", pprof.Handler("threadcreate")).Methods(http.MethodGet)
	r.Mux.Handle("/debug/pprof/block", pprof.Handler("block")).Methods(http.MethodGet)
}
