package server

import (
	"encoding/json"
	"fmt"
	"net"
	"net/http"

	"github.com/sirupsen/logrus"
	"markdown-editor-backend/handlers"
	"markdown-editor-backend/storage"
)

// Server represents the HTTP server
type Server struct {
	dataDir    string
	host       string
	port       int
	log        *logrus.Logger
	storage    *storage.Storage
	handler    *handlers.Handler
	staticPath string
	listener   net.Listener
}

// New creates a new Server instance
func New(dataDir, host string, port int, log *logrus.Logger) *Server {
	store := storage.New(dataDir, log)
	handler := handlers.New(store, log)
	return &Server{
		dataDir:    dataDir,
		host:       host,
		port:       port,
		log:        log,
		storage:    store,
		handler:    handler,
		staticPath: "./static",
	}
}

// SetStaticPath sets the path for static files (used for embedding frontend)
func (s *Server) SetStaticPath(path string) {
	s.staticPath = path
}

// Start initializes storage and starts the HTTP server
func (s *Server) Start() error {
	if err := s.storage.Init(); err != nil {
		return err
	}

	mux := http.NewServeMux()

	// API routes
	mux.HandleFunc("/api/files", s.handler.FilesHandler)
	mux.HandleFunc("/api/files/", s.handler.FileHandler)

	// Health check
	mux.HandleFunc("/api/health", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		json.NewEncoder(w).Encode(map[string]string{"status": "ok"})
	})

	// Static file serving - serve frontend build
	fs := http.FileServer(http.Dir(s.staticPath))
	mux.Handle("/", fs)

	// CORS middleware
	handler := s.corsMiddleware(mux)

	addr := fmt.Sprintf("%s:%d", s.host, s.port)
	s.log.WithField("address", addr).Info("Server starting")

	listener, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}
	s.listener = listener

	return http.Serve(listener, handler)
}

// StartAsync starts the server asynchronously and returns the actual address
func (s *Server) StartAsync() (string, error) {
	if err := s.storage.Init(); err != nil {
		return "", err
	}

	mux := http.NewServeMux()

	// API routes
	mux.HandleFunc("/api/files", s.handler.FilesHandler)
	mux.HandleFunc("/api/files/", s.handler.FileHandler)

	// Health check
	mux.HandleFunc("/api/health", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		json.NewEncoder(w).Encode(map[string]string{"status": "ok"})
	})

	// Static file serving - serve frontend build
	fs := http.FileServer(http.Dir(s.staticPath))
	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if len(r.URL.Path) >= 4 && r.URL.Path[:4] == "/api" {
			return
		}
		fs.ServeHTTP(w, r)
	})

	// CORS middleware
	handler := s.corsMiddleware(mux)

	addr := fmt.Sprintf("%s:%d", s.host, s.port)
	if s.port == 0 {
		addr = fmt.Sprintf("%s:0", s.host)
	}

	listener, err := net.Listen("tcp", addr)
	if err != nil {
		return "", err
	}
	s.listener = listener

	actualAddr := listener.Addr().String()
	s.log.WithField("address", actualAddr).Info("Server starting")

	go http.Serve(listener, handler)

	return actualAddr, nil
}

// Stop stops the server
func (s *Server) Stop() error {
	if s.listener != nil {
		return s.listener.Close()
	}
	return nil
}

// corsMiddleware adds CORS headers for frontend development
func (s *Server) corsMiddleware(next 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, DELETE, OPTIONS")
		w.Header().Set("Access-Control-Allow-Headers", "Content-Type")

		if r.Method == "OPTIONS" {
			w.WriteHeader(http.StatusOK)
			return
		}

		next.ServeHTTP(w, r)
	})
}

// GetStorage returns the storage instance (for testing)
func (s *Server) GetStorage() *storage.Storage {
	return s.storage
}

// GetStaticPath returns the static files path (for testing)
func (s *Server) GetStaticPath() string {
	return s.staticPath
}

// GetAddr returns the server address (for testing)
func (s *Server) GetAddr() string {
	if s.listener != nil {
		return s.listener.Addr().String()
	}
	return fmt.Sprintf("%s:%d", s.host, s.port)
}
