package main

import (
	"context"
	"embed"
	"evening_detective/internal/app"
	"evening_detective/internal/config"
	"evening_detective/internal/modules/cleaner"
	"evening_detective/internal/modules/formatter"
	"evening_detective/internal/modules/password"
	"evening_detective/internal/services"
	"evening_detective/internal/services/db"
	"evening_detective/internal/services/link"
	"evening_detective/internal/services/pdf"
	"evening_detective/internal/services/story_service"
	"evening_detective/internal/services/story_storage"
	proto "evening_detective/proto"
	"io/fs"
	"log"
	"net"
	"net/http"
	"time"

	"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	"google.golang.org/grpc/metadata"
)

//go:embed static/user/*
var userFS embed.FS

//go:embed static/admin/*
var adminFS embed.FS

func main() {
	// Create a listener on TCP port
	lis, err := net.Listen("tcp", ":8080")
	if err != nil {
		log.Fatalln("Failed to listen:", err)
	}

	// Create a gRPC server object
	s := grpc.NewServer()
	// Attach the Greeter service to the server
	dbFilepath := config.GetDBFilepath()

	dbService, err := db.NewDBService(dbFilepath)
	if err != nil {
		log.Fatalln(err)
	}

	clientHost := config.GetHost()
	fileHost := config.GetFileHost()

	cleaner := cleaner.NewCleaner()

	formatter := formatter.NewFormatter()

	storyFilepath := config.GetStoryFilepath()

	storyStorage := story_storage.NewFileStoryStorage(storyFilepath)

	storyService, err := story_service.NewStoryService(
		cleaner,
		formatter,
		storyStorage,
		link.NewLinkService(fileHost),
	)
	if err != nil {
		log.Fatalln(err)
	}

	linkService := link.NewLinkService(clientHost)

	passwordGenerator := password.NewPasswordGenerator()

	pdfGenerator := pdf.NewPDFGenerator()

	proto.RegisterEveningDetectiveServer(
		s,
		app.NewServer(
			services.NewServices(
				dbService,
				storyService,
				linkService,
				passwordGenerator,
				pdfGenerator,
			),
		),
	)
	// Serve gRPC server
	log.Println("Serving gRPC on 0.0.0.0:8080")
	go func() {
		log.Fatalln(s.Serve(lis))
	}()

	// Create a client connection to the gRPC server we just started
	// This is where the gRPC-Gateway proxies the requests
	conn, err := grpc.NewClient(
		"0.0.0.0:8080",
		grpc.WithTransportCredentials(insecure.NewCredentials()),
	)
	if err != nil {
		log.Fatalln("Failed to dial server:", err)
	}

	gwmux := runtime.NewServeMux(
		runtime.WithMetadata(func(ctx context.Context, request *http.Request) metadata.MD {
			teamId := request.Header.Get("X-Id")
			return metadata.Pairs("team-id", teamId)
		}),
		runtime.WithMetadata(func(ctx context.Context, request *http.Request) metadata.MD {
			password := request.Header.Get("X-Password")
			return metadata.Pairs("password", password)
		}),
	)
	// Register Greeter
	err = proto.RegisterEveningDetectiveHandler(context.Background(), gwmux, conn)
	if err != nil {
		log.Fatalln("Failed to register gateway:", err)
	}

	gwServer := &http.Server{
		Addr:    ":8090",
		Handler: cors(gwmux),
	}

	// Serve gRPC-Gateway server
	log.Println("Serving gRPC-Gateway on http://0.0.0.0:8090")
	go func() {
		log.Fatalln(gwServer.ListenAndServe())
	}()

	muxUser := http.NewServeMux()
	subUserFS, err := fs.Sub(userFS, "static/user")
	if err != nil {
		log.Fatalln(err)
	}
	fileServerUser := http.FileServer(http.FS(subUserFS))
	muxUser.Handle("/", fileServerUser)

	// Serve user web server
	log.Println("Serving user web on http://0.0.0.0" + config.ClientPort)
	go func() {
		log.Fatalln(http.ListenAndServe(config.ClientPort, muxUser))
	}()

	go func() {
		dir := "./data/story/images"
		// Создаем файловый сервер
		fs := http.FileServer(http.Dir(dir))

		// Добавляем middleware для логирования
		http.Handle("/", loggingMiddleware(fs))

		log.Println("Файловый сервер запущен на http://localhost:8120")
		log.Println("Обслуживается директория: " + dir)
		log.Fatal(http.ListenAndServe(":8120", nil))
	}()

	muxAdmin := http.NewServeMux()
	subAdminFS, err := fs.Sub(adminFS, "static/admin")
	if err != nil {
		log.Fatalln(err)
	}
	fileServerAdmin := http.FileServer(http.FS(subAdminFS))
	muxAdmin.Handle("/", fileServerAdmin)

	// Serve admin web server
	log.Println("Serving admin web on http://0.0.0.0:8110")
	log.Fatalln(http.ListenAndServe(":8110", muxAdmin))
}

func cors(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Access-Control-Allow-Origin", "*")
		w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE")
		w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, Authorization, ResponseType, X-Id, X-Password")
		if r.Method == "OPTIONS" {
			return
		}
		h.ServeHTTP(w, r)
	})
}

func loggingMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()

		// Логируем запрос
		log.Printf("[%s] %s %s", r.Method, r.URL.Path, time.Since(start))

		next.ServeHTTP(w, r)
	})
}
