package handlers

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"strconv"
	"strings"

	"fred-api/config"
	"fred-api/models"

	"github.com/gin-gonic/gin"
)

type FREDHandler struct {
	baseURL string
	apiKey  string
}

func NewFREDHandler(cfg *config.Config) *FREDHandler {
	return &FREDHandler{
		baseURL: cfg.FREDBaseURL,
		apiKey:  cfg.FREDAPIKey,
	}
}

func (h *FREDHandler) buildURL(endpoint string, params map[string]string) string {
	u, _ := url.Parse(h.baseURL + endpoint)
	query := u.Query()
	query.Set("api_key", h.apiKey)
	query.Set("file_type", "json")

	for k, v := range params {
		if v != "" {
			query.Set(k, v)
		}
	}

	u.RawQuery = query.Encode()
	return u.String()
}

func (h *FREDHandler) fetchData(url string, target interface{}) error {
	resp, err := http.Get(url)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	if resp.StatusCode != http.StatusOK {
		var errResp models.ErrorResponse
		json.Unmarshal(body, &errResp)
		return fmt.Errorf("API error: %s - %s", errResp.ErrorCode, errResp.ErrorMessage)
	}

	return json.Unmarshal(body, target)
}

func (h *FREDHandler) GetSeries(c *gin.Context) {
	seriesID := c.Query("series_id")
	if seriesID == "" {
		c.JSON(http.StatusBadRequest, models.ErrorResponse{
			ErrorCode:    "BadRequest",
			ErrorMessage: "series_id is required",
		})
		return
	}

	params := map[string]string{
		"series_id": seriesID,
	}

	if realtimeStart := c.Query("realtime_start"); realtimeStart != "" {
		params["realtime_start"] = realtimeStart
	}
	if realtimeEnd := c.Query("realtime_end"); realtimeEnd != "" {
		params["realtime_end"] = realtimeEnd
	}

	url := h.buildURL("/series", params)

	var response models.SeriesListResponse
	if err := h.fetchData(url, &response); err != nil {
		c.JSON(http.StatusInternalServerError, models.ErrorResponse{
			ErrorCode:    "InternalError",
			ErrorMessage: err.Error(),
		})
		return
	}

	c.JSON(http.StatusOK, response)
}

func (h *FREDHandler) GetObservations(c *gin.Context) {
	seriesID := c.Query("series_id")
	if seriesID == "" {
		c.JSON(http.StatusBadRequest, models.ErrorResponse{
			ErrorCode:    "BadRequest",
			ErrorMessage: "series_id is required",
		})
		return
	}

	params := map[string]string{
		"series_id": seriesID,
	}

	optionalParams := []string{
		"realtime_start", "realtime_end", "limit", "offset", "sort_order",
		"observation_start", "observation_end", "units", "frequency",
		"aggregation_method", "output_type", "vintage_dates",
	}

	for _, param := range optionalParams {
		if v := c.Query(param); v != "" {
			params[param] = v
		}
	}

	url := h.buildURL("/series/observations", params)

	var response models.ObservationsResponse
	if err := h.fetchData(url, &response); err != nil {
		c.JSON(http.StatusInternalServerError, models.ErrorResponse{
			ErrorCode:    "InternalError",
			ErrorMessage: err.Error(),
		})
		return
	}

	c.JSON(http.StatusOK, response)
}

func (h *FREDHandler) SearchSeries(c *gin.Context) {
	searchText := c.Query("search_text")
	if searchText == "" {
		c.JSON(http.StatusBadRequest, models.ErrorResponse{
			ErrorCode:    "BadRequest",
			ErrorMessage: "search_text is required",
		})
		return
	}

	params := map[string]string{
		"search_text": searchText,
	}

	optionalParams := []string{
		"realtime_start", "realtime_end", "limit", "offset", "sort_order",
		"observation_start", "observation_end", "units", "frequency",
		"aggregation_method", "output_type",
	}

	for _, param := range optionalParams {
		if v := c.Query(param); v != "" {
			params[param] = v
		}
	}

	url := h.buildURL("/series/search", params)

	var response models.SearchResponse
	if err := h.fetchData(url, &response); err != nil {
		c.JSON(http.StatusInternalServerError, models.ErrorResponse{
			ErrorCode:    "InternalServerError",
			ErrorMessage: err.Error(),
		})
		return
	}

	c.JSON(http.StatusOK, response)
}

func (h *FREDHandler) GetReleases(c *gin.Context) {
	params := map[string]string{}

	optionalParams := []string{
		"realtime_start", "realtime_end", "limit", "offset", "sort_order",
	}

	for _, param := range optionalParams {
		if v := c.Query(param); v != "" {
			params[param] = v
		}
	}

	url := h.buildURL("/releases", params)

	var response models.ReleasesResponse
	if err := h.fetchData(url, &response); err != nil {
		c.JSON(http.StatusInternalServerError, models.ErrorResponse{
			ErrorCode:    "InternalServerError",
			ErrorMessage: err.Error(),
		})
		return
	}

	c.JSON(http.StatusOK, response)
}

func (h *FREDHandler) GetReleaseDates(c *gin.Context) {
	params := map[string]string{}

	optionalParams := []string{
		"realtime_start", "realtime_end", "limit", "offset", "sort_order",
		"release_id", "date", "start_date", "end_date",
	}

	for _, param := range optionalParams {
		if v := c.Query(param); v != "" {
			params[param] = v
		}
	}

	url := h.buildURL("/releases/dates", params)

	var response models.ReleaseDatesResponse
	if err := h.fetchData(url, &response); err != nil {
		c.JSON(http.StatusInternalServerError, models.ErrorResponse{
			ErrorCode:    "InternalServerError",
			ErrorMessage: err.Error(),
		})
		return
	}

	c.JSON(http.StatusOK, response)
}

func (h *FREDHandler) GetReleaseSeries(c *gin.Context) {
	releaseIDStr := c.Query("release_id")
	if releaseIDStr == "" {
		c.JSON(http.StatusBadRequest, models.ErrorResponse{
			ErrorCode:    "BadRequest",
			ErrorMessage: "release_id is required",
		})
		return
	}

	releaseID, err := strconv.Atoi(releaseIDStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, models.ErrorResponse{
			ErrorCode:    "BadRequest",
			ErrorMessage: "release_id must be a number",
		})
		return
	}

	params := map[string]string{
		"release_id": strings.TrimSpace(strings.Replace(strconv.Itoa(releaseID), " ", "", -1)),
	}

	optionalParams := []string{
		"realtime_start", "realtime_end", "limit", "offset", "sort_order",
		"observation_start", "observation_end", "units", "frequency",
		"aggregation_method", "output_type",
	}

	for _, param := range optionalParams {
		if v := c.Query(param); v != "" {
			params[param] = v
		}
	}

	url := h.buildURL("/release/series", params)

	var response models.SearchResponse
	if err := h.fetchData(url, &response); err != nil {
		c.JSON(http.StatusInternalServerError, models.ErrorResponse{
			ErrorCode:    "InternalServerError",
			ErrorMessage: err.Error(),
		})
		return
	}

	c.JSON(http.StatusOK, response)
}

func (h *FREDHandler) HealthCheck(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"status":  "healthy",
		"service": "FRED API Proxy",
	})
}
