package api

import (
	"context"
	"database/sql"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"strconv"
	"strings"

	"deployment-manager/internal/db"
	"deployment-manager/internal/events"
	"deployment-manager/internal/model"
)

type HTTPServer struct {
	repoStore  *db.RepoStore
	eventBus   *events.Bus
	server     *http.Server
}

func NewHTTPServer(database *sql.DB, eventBus *events.Bus) *HTTPServer {
	return &HTTPServer{
		repoStore:  db.NewRepoStore(database),
		eventBus:   eventBus,
	}
}

func (s *HTTPServer) Start(addr string) error {
	mux := http.NewServeMux()
	
	// API routes
	mux.HandleFunc("/api/repos", s.handleRepos)
	mux.HandleFunc("/api/repos/", s.handleRepo)
	mux.HandleFunc("/api/repos/", s.handleRepoActions)
	
	// SSE endpoint
	sseHandler := NewSSEHandler(s.eventBus)
	mux.Handle("/events", sseHandler)
	
	// Health check
	mux.HandleFunc("/health", s.handleHealth)
	
	s.server = &http.Server{
		Addr:    addr,
		Handler: mux,
	}
	
	log.Printf("HTTP server starting on %s", addr)
	return s.server.ListenAndServe()
}

func (s *HTTPServer) Shutdown(ctx context.Context) error {
	if s.server != nil {
		return s.server.Shutdown(ctx)
	}
	return nil
}

func (s *HTTPServer) handleRepos(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case http.MethodGet:
		s.getRepos(w, r)
	case http.MethodPost:
		s.createRepo(w, r)
	default:
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
	}
}

func (s *HTTPServer) handleRepo(w http.ResponseWriter, r *http.Request) {
	repoID, err := extractRepoID(r.URL.Path)
	if err != nil {
		http.Error(w, "Invalid repo ID", http.StatusBadRequest)
		return
	}
	
	switch r.Method {
	case http.MethodGet:
		s.getRepo(w, r, repoID)
	case http.MethodDelete:
		s.deleteRepo(w, r, repoID)
	default:
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
	}
}

func (s *HTTPServer) handleRepoActions(w http.ResponseWriter, r *http.Request) {
	repoID, err := extractRepoID(r.URL.Path)
	if err != nil {
		http.Error(w, "Invalid repo ID", http.StatusBadRequest)
		return
	}
	
	action := extractAction(r.URL.Path)
	
	switch r.Method {
	case http.MethodPost:
		switch action {
		case "stop":
			s.stopRepo(w, r, repoID)
		case "restart":
			s.restartRepo(w, r, repoID)
		default:
			http.Error(w, "Invalid action", http.StatusBadRequest)
		}
	default:
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
	}
}

func (s *HTTPServer) getRepos(w http.ResponseWriter, r *http.Request) {
	userID := r.URL.Query().Get("user_id")
	
	var repos []*model.Repo
	var err error
	
	if userID != "" {
		repos, err = s.repoStore.ListByUser(userID)
	} else {
		repos, err = s.repoStore.ListByStatus(model.StatusDeployed)
	}
	
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(repos)
}

func (s *HTTPServer) getRepo(w http.ResponseWriter, r *http.Request, repoID int64) {
	repo, err := s.repoStore.Get(repoID)
	if err != nil {
		http.Error(w, err.Error(), http.StatusNotFound)
		return
	}
	
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(repo)
}

func (s *HTTPServer) createRepo(w http.ResponseWriter, r *http.Request) {
	var req struct {
		RepoURL string        `json:"repo_url"`
		UserID  string        `json:"user_id"`
		Type    model.RepoType `json:"type"`
	}
	
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, "Invalid JSON", http.StatusBadRequest)
		return
	}
	
	repo := &model.Repo{
		RepoURL: req.RepoURL,
		Status:  model.StatusNeedToDeploy,
		UserID:  req.UserID,
		Type:    req.Type,
	}
	
	if err := s.repoStore.Create(repo); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	
	// Publish event
	s.eventBus.Publish(events.NewRepoEvent(repo, events.EventTypeRepoCreated))
	
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusCreated)
	json.NewEncoder(w).Encode(repo)
}

func (s *HTTPServer) deleteRepo(w http.ResponseWriter, r *http.Request, repoID int64) {
	repo, err := s.repoStore.Get(repoID)
	if err != nil {
		http.Error(w, err.Error(), http.StatusNotFound)
		return
	}
	
	// Update status to deleted for cleanup
	repo.Status = model.StatusDeleted
	if err := s.repoStore.Update(repo); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	
	// Publish event
	s.eventBus.Publish(events.NewRepoEvent(repo, events.EventTypeRepoDeleted))
	
	w.WriteHeader(http.StatusNoContent)
}

func (s *HTTPServer) stopRepo(w http.ResponseWriter, r *http.Request, repoID int64) {
	repo, err := s.repoStore.Get(repoID)
	if err != nil {
		http.Error(w, err.Error(), http.StatusNotFound)
		return
	}
	
	repo.Status = model.StatusStopped
	if err := s.repoStore.Update(repo); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	
	// Publish event
	s.eventBus.Publish(events.NewRepoEvent(repo, events.EventTypeRepoUpdated))
	
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(repo)
}

func (s *HTTPServer) restartRepo(w http.ResponseWriter, r *http.Request, repoID int64) {
	repo, err := s.repoStore.Get(repoID)
	if err != nil {
		http.Error(w, err.Error(), http.StatusNotFound)
		return
	}
	
	repo.Status = model.StatusRestarting
	if err := s.repoStore.Update(repo); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	
	// Publish event
	s.eventBus.Publish(events.NewRepoEvent(repo, events.EventTypeRepoUpdated))
	
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(repo)
}

func extractRepoID(path string) (int64, error) {
	parts := strings.Split(path, "/")
	if len(parts) < 4 {
		return 0, fmt.Errorf("invalid path")
	}
	
	return strconv.ParseInt(parts[3], 10, 64)
}

func extractAction(path string) string {
	parts := strings.Split(path, "/")
	if len(parts) >= 5 {
		return parts[4]
	}
	return ""
}
