package api

import (
	"encoding/json"
	"fmt"
	"net/http"
	"path/filepath"
	"strings"

	"github.com/markdown-editor/internal/storage"
	"github.com/markdown-editor/pkg/logger"
)

type ErrorResponse struct {
	Error string `json:"error"`
}

type Server struct {
	storage *storage.Storage
	host    string
	port    int
}

func NewServer(dataDir, host string, port int) (*Server, error) {
	return &Server{
		storage: storage.NewStorage(dataDir),
		host:    host,
		port:    port,
	}, nil
}

func (s *Server) Start() error {
	mux := http.NewServeMux()

	// API routes
	mux.HandleFunc("/api/files", s.handleFiles)
	mux.HandleFunc("/api/files/", s.handleFiles)

	// Static files
	mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./static"))))

	// Frontend SPA fallback
	mux.HandleFunc("/", s.handleFrontend)

	addr := fmt.Sprintf("%s:%d", s.host, s.port)
	logger.Infof("Starting server on %s", addr)

	return http.ListenAndServe(addr, mux)
}

func (s *Server) handleFiles(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case http.MethodGet:
		s.handleGetFiles(w, r)
	case http.MethodPost:
		s.handleCreateFile(w, r)
	case http.MethodPut:
		s.handleUpdateFile(w, r)
	case http.MethodDelete:
		s.handleDeleteFile(w, r)
	default:
		s.sendError(w, http.StatusMethodNotAllowed, "method not allowed")
	}
}

func (s *Server) handleGetFiles(w http.ResponseWriter, r *http.Request) {
	// Check if URL includes a filename (e.g., /api/files/test.md)
	filename := filepath.Base(r.URL.Path)
	if filename != "" && r.URL.Path != "/api/files" {
		// Get specific file
		content, err := s.storage.GetFile(filename)
		if err != nil {
			s.sendError(w, http.StatusNotFound, err.Error())
			return
		}
		w.Header().Set("Content-Type", "text/markdown")
		w.Write([]byte(content))
		return
	}

	// List all files
	files, err := s.storage.ListFiles()
	if err != nil {
		s.sendError(w, http.StatusInternalServerError, err.Error())
		return
	}

	w.Header().Set("Content-Type", "application/json")
	w.Write([]byte{'['})
	for i, file := range files {
		if i > 0 {
			w.Write([]byte{','})
		}
		w.Write([]byte{'"'})
		w.Write([]byte(file))
		w.Write([]byte{'"'})
	}
	w.Write([]byte{']'})
}

func (s *Server) handleCreateFile(w http.ResponseWriter, r *http.Request) {
	var req struct {
		Content string `json:"content"`
		Name    string `json:"name"`
	}

	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		s.sendError(w, http.StatusBadRequest, "invalid request body")
		return
	}

	if req.Name == "" {
		s.sendError(w, http.StatusBadRequest, "filename is required")
		return
	}

	if !strings.HasSuffix(req.Name, ".md") {
		s.sendError(w, http.StatusBadRequest, "filename must end with .md")
		return
	}

	if err := s.storage.SaveFile(req.Name, req.Content); err != nil {
		s.sendError(w, http.StatusInternalServerError, err.Error())
		return
	}

	logger.Infof("Created file: %s", req.Name)
	w.WriteHeader(http.StatusCreated)
}

func (s *Server) handleUpdateFile(w http.ResponseWriter, r *http.Request) {
	filename := filepath.Base(r.URL.Path)

	if filename == "" {
		s.sendError(w, http.StatusBadRequest, "filename is required")
		return
	}

	if !strings.HasSuffix(filename, ".md") {
		s.sendError(w, http.StatusBadRequest, "filename must end with .md")
		return
	}

	var req struct {
		Content string `json:"content"`
	}

	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		s.sendError(w, http.StatusBadRequest, "invalid request body")
		return
	}

	if err := s.storage.SaveFile(filename, req.Content); err != nil {
		s.sendError(w, http.StatusInternalServerError, err.Error())
		return
	}

	logger.Infof("Updated file: %s", filename)
}

func (s *Server) handleDeleteFile(w http.ResponseWriter, r *http.Request) {
	filename := filepath.Base(r.URL.Path)

	if filename == "" {
		s.sendError(w, http.StatusBadRequest, "filename is required")
		return
	}

	if !strings.HasSuffix(filename, ".md") {
		s.sendError(w, http.StatusBadRequest, "filename must end with .md")
		return
	}

	if err := s.storage.DeleteFile(filename); err != nil {
		s.sendError(w, http.StatusInternalServerError, err.Error())
		return
	}

	logger.Infof("Deleted file: %s", filename)
}

func (s *Server) handleFrontend(w http.ResponseWriter, r *http.Request) {
	// Serve the index.html for SPA
	http.ServeFile(w, r, "./static/index.html")
}

func (s *Server) sendError(w http.ResponseWriter, statusCode int, message string) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(statusCode)
	json.NewEncoder(w).Encode(ErrorResponse{Error: message})
}

func (s *Server) ServeStaticFiles(w http.ResponseWriter, r *http.Request) {
	http.StripPrefix("/static/", http.FileServer(http.Dir("./static"))).ServeHTTP(w, r)
}
