package handler

import (
	"fmt"
	"html/template"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"podstalk/store"
)

const maxUploadSize = 200 << 20 // 200 MB

type EpisodeHandler struct {
	Store     *store.Store
	Tpl       *Templates
	UploadDir string
	BaseURL   string
}

func (h *EpisodeHandler) ServeDashboard(w http.ResponseWriter, r *http.Request) {
	userID := UserIDFromContext(r)
	user, err := h.Store.GetUser(userID)
	if err != nil {
		http.Error(w, "user not found", http.StatusInternalServerError)
		return
	}

	episodes, err := h.Store.ListEpisodes(userID)
	if err != nil {
		http.Error(w, "failed to load episodes", http.StatusInternalServerError)
		return
	}

	title := user.PodcastTitle
	if title == "" {
		title = "Podstalk"
	}
	type epView struct {
		ID          int64
		Title       string
		Description string
		AudioPath   string
		ImageURL    template.URL
		PublishedAt time.Time
	}
	var epViews []epView
	for _, ep := range episodes {
		ev := epView{
			ID:          ep.ID,
			Title:       ep.Title,
			Description: ep.Description,
			AudioPath:   ep.AudioPath,
			PublishedAt: ep.PublishedAt,
		}
		if ep.ImagePath != "" {
			ev.ImageURL = template.URL("/uploads/" + ep.ImagePath)
		} else if user.PodcastImage != "" {
			ev.ImageURL = template.URL("/uploads/" + user.PodcastImage)
		}
		epViews = append(epViews, ev)
	}

	podcastImageURL := template.URL("")
	if user.PodcastImage != "" {
		podcastImageURL = template.URL("/uploads/" + user.PodcastImage)
	}

	h.Tpl.Render(w, "dashboard", map[string]any{
		"Episodes":        epViews,
		"PodcastTitle":    title,
		"PodcastSlug":     user.PodcastSlug,
		"PodcastImageURL": podcastImageURL,
		"PodcastAuthor":   user.PodcastAuthor,
		"RSSURL":          template.URL(h.BaseURL + "/rss/" + user.PodcastSlug),
	})
}
func (h *EpisodeHandler) ServeNew(w http.ResponseWriter, r *http.Request) {
	userID := UserIDFromContext(r)
	if r.Method == http.MethodGet {
		h.Tpl.Render(w, "episode_form", map[string]any{"Editing": false})
		return
	}

	if r.Method == http.MethodPost {
		if err := r.ParseMultipartForm(maxUploadSize); err != nil {
			h.Tpl.Render(w, "episode_form", map[string]any{
				"Editing": false,
				"Error":   "File too large (max 200 MB).",
			})
			return
		}

		title := r.FormValue("title")
		description := r.FormValue("description")
		publishedStr := r.FormValue("published_at")

		publishedAt := time.Now()
		if publishedStr != "" {
			if t, err := time.Parse("2006-01-02T15:04", publishedStr); err == nil {
				publishedAt = t
			}
		}

		if title == "" {
			h.Tpl.Render(w, "episode_form", map[string]any{
				"Editing": false,
				"Error":   "Title is required.",
			})
			return
		}

		audioPath, err := h.saveUploadedFile(r, "audio_file", "audio")
		if err != nil {
			h.Tpl.Render(w, "episode_form", map[string]any{
				"Editing": false,
				"Error":   fmt.Sprintf("Audio upload failed: %v", err),
			})
			return
		}

		imagePath, _ := h.saveUploadedFile(r, "image_file", "images")

		_, err = h.Store.CreateEpisode(userID, title, description, audioPath, imagePath, publishedAt)
		if err != nil {
			h.Tpl.Render(w, "episode_form", map[string]any{
				"Editing": false,
				"Error":   "Failed to create episode.",
			})
			return
		}
		http.Redirect(w, r, "/?success=episode+created", http.StatusSeeOther)
	}
}

func (h *EpisodeHandler) ServeEdit(w http.ResponseWriter, r *http.Request) {
	idStr := r.URL.Query().Get("id")
	id, err := strconv.ParseInt(idStr, 10, 64)
	if err != nil {
		http.Error(w, "invalid id", http.StatusBadRequest)
		return
	}

	if r.Method == http.MethodGet {
		ep, err := h.Store.GetEpisode(id)
		if err != nil {
			http.Error(w, "episode not found", http.StatusNotFound)
			return
		}
		h.Tpl.Render(w, "episode_form", map[string]any{
			"Editing":     true,
			"Episode":     ep,
			"PublishedAt": ep.PublishedAt.Format("2006-01-02T15:04"),
		})
		return
	}

	if r.Method == http.MethodPost {
		if err := r.ParseMultipartForm(maxUploadSize); err != nil {
			http.Error(w, "file too large", http.StatusRequestEntityTooLarge)
			return
		}

		title := r.FormValue("title")
		description := r.FormValue("description")
		publishedStr := r.FormValue("published_at")

		publishedAt := time.Now()
		if publishedStr != "" {
			if t, err := time.Parse("2006-01-02T15:04", publishedStr); err == nil {
				publishedAt = t
			}
		}

		if title == "" {
			ep, _ := h.Store.GetEpisode(id)
			h.Tpl.Render(w, "episode_form", map[string]any{
				"Editing":     true,
				"Episode":     ep,
				"PublishedAt": publishedStr,
				"Error":       "Title is required.",
			})
			return
		}

		existing, err := h.Store.GetEpisode(id)
		if err != nil {
			http.Error(w, "episode not found", http.StatusNotFound)
			return
		}

		audioPath := existing.AudioPath
		if newPath, err := h.saveUploadedFile(r, "audio_file", "audio"); err == nil && newPath != "" {
			h.removeUploadedFile(audioPath)
			audioPath = newPath
		}

		imagePath := existing.ImagePath
		if newPath, err := h.saveUploadedFile(r, "image_file", "images"); err == nil && newPath != "" {
			h.removeUploadedFile(imagePath)
			imagePath = newPath
		}

		if err := h.Store.UpdateEpisode(id, title, description, audioPath, imagePath, publishedAt); err != nil {
			http.Error(w, "update failed", http.StatusInternalServerError)
			return
		}
		http.Redirect(w, r, "/?success=episode+updated", http.StatusSeeOther)
	}
}

func (h *EpisodeHandler) ServeDelete(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
		return
	}

	idStr := r.URL.Query().Get("id")
	id, err := strconv.ParseInt(idStr, 10, 64)
	if err != nil {
		http.Error(w, "invalid id", http.StatusBadRequest)
		return
	}

	ep, err := h.Store.GetEpisode(id)
	if err == nil {
		h.removeUploadedFile(ep.AudioPath)
		h.removeUploadedFile(ep.ImagePath)
	}

	if err := h.Store.DeleteEpisode(id); err != nil {
		http.Error(w, "delete failed", http.StatusInternalServerError)
		return
	}
	http.Redirect(w, r, "/?success=episode+deleted", http.StatusSeeOther)
}

func (h *EpisodeHandler) ServeUpload(w http.ResponseWriter, r *http.Request) {
	relPath := strings.TrimPrefix(r.URL.Path, "/uploads/")
	if relPath == "" || strings.Contains(relPath, "..") {
		http.Error(w, "invalid path", http.StatusBadRequest)
		return
	}
	http.ServeFile(w, r, filepath.Join(h.UploadDir, relPath))
}

func (h *EpisodeHandler) saveUploadedFile(r *http.Request, field, category string) (string, error) {
	file, header, err := r.FormFile(field)
	if err != nil {
		return "", err
	}
	defer file.Close()

	base := sanitizeFilename(header.Filename)
	ext := filepath.Ext(base)
	if ext == "" {
		ext = ".bin"
	} else {
		base = base[:len(base)-len(ext)]
	}
	filename := fmt.Sprintf("%d_%s%s", time.Now().UnixNano(), base, ext)

	catDir := filepath.Join(h.UploadDir, category)
	if err := os.MkdirAll(catDir, 0755); err != nil {
		return "", err
	}

	dst, err := os.Create(filepath.Join(catDir, filename))
	if err != nil {
		return "", err
	}
	defer dst.Close()

	if _, err := io.Copy(dst, file); err != nil {
		return "", err
	}

	return category + "/" + filename, nil
}

func (h *EpisodeHandler) removeUploadedFile(relPath string) {
	if relPath == "" {
		return
	}
	os.Remove(filepath.Join(h.UploadDir, relPath))
}

func sanitizeFilename(name string) string {
	name = filepath.Base(name)
	name = strings.Map(func(r rune) rune {
		if r >= 'a' && r <= 'z' || r >= '0' && r <= '9' || r == '.' || r == '-' {
			return r
		}
		if r >= 'A' && r <= 'Z' {
			return r + 32
		}
		return -1
	}, name)
	return name
}
