package services

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"time"
	"fotbal-club/internal/config"
	"fotbal-club/pkg/logger"
)

type UmamiService struct {
	baseURL       string
	username      string
	password      string
	token         string
	tokenExp      time.Time
	lastVerified  time.Time
	defaultWebsiteID string
}

// --- Helpers for website discovery/creation and event sending ---

// UmamiWebsite represents a single website entry as returned by the list API
type UmamiWebsite struct {
	ID     string `json:"id"`
	Name   string `json:"name"`
	Domain string `json:"domain"`
}

// umamiListWebsitesResponse matches the shape of GET /api/websites
type umamiListWebsitesResponse struct {
	Data     []UmamiWebsite `json:"data"`
	Count    int            `json:"count"`
	Page     int            `json:"page"`
	PageSize int            `json:"pageSize"`
}

var ErrUmamiNoWebsites = errors.New("no websites found in Umami")

// FindWebsiteIDByDomain returns the website ID for a given domain if it exists.
func (u *UmamiService) FindWebsiteIDByDomain(domain string) (string, error) {
	if err := u.authenticate(); err != nil {
		return "", err
	}

	url := fmt.Sprintf("%s/api/websites?page=1&pageSize=100&orderBy=name&query=%s", u.baseURL, domain)
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return "", fmt.Errorf("failed to create list websites request: %w", err)
	}

	req.Header.Set("Authorization", "Bearer "+u.token)
	client := &http.Client{Timeout: 10 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return "", fmt.Errorf("failed to send list websites request: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		bodyBytes, _ := io.ReadAll(resp.Body)
		return "", fmt.Errorf("list websites failed with status %d: %s", resp.StatusCode, string(bodyBytes))
	}

	var list umamiListWebsitesResponse
	if err := json.NewDecoder(resp.Body).Decode(&list); err != nil {
		return "", fmt.Errorf("failed to decode list websites response: %w", err)
	}

	for _, w := range list.Data {
		if w.Domain == domain {
			return w.ID, nil
		}
	}

	return "", nil
}

// GetDefaultWebsiteID returns the first available website ID from Umami
func (u *UmamiService) GetDefaultWebsiteID() (string, error) {
	logger.Info("Attempting to get default Umami website ID from %s", u.baseURL)
	
	if err := u.authenticate(); err != nil {
		return "", fmt.Errorf("authentication failed: %w", err)
	}

	url := fmt.Sprintf("%s/api/websites?page=1&pageSize=1&orderBy=name", u.baseURL)
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return "", fmt.Errorf("failed to create list websites request: %w", err)
	}

	req.Header.Set("Authorization", "Bearer "+u.token)
	client := &http.Client{Timeout: 10 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return "", fmt.Errorf("failed to send list websites request: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		bodyBytes, _ := io.ReadAll(resp.Body)
		return "", fmt.Errorf("list websites failed with status %d: %s", resp.StatusCode, string(bodyBytes))
	}

	var list umamiListWebsitesResponse
	if err := json.NewDecoder(resp.Body).Decode(&list); err != nil {
		return "", fmt.Errorf("failed to decode list websites response: %w", err)
	}

	if len(list.Data) == 0 {
		logger.Warn("No websites found in Umami instance at %s. Please create a website first.", u.baseURL)
		return "", ErrUmamiNoWebsites
	}

	u.defaultWebsiteID = list.Data[0].ID
	logger.Info("Found default Umami website: ID=%s, Name=%s, Domain=%s", list.Data[0].ID, list.Data[0].Name, list.Data[0].Domain)
	return list.Data[0].ID, nil
}

// EnsureWebsite returns an existing website ID for the domain or creates a new one.
func (u *UmamiService) EnsureWebsite(name, domain string) (string, error) {
	if domain == "" {
		return "", fmt.Errorf("domain is required")
	}

	if err := u.authenticate(); err != nil {
		return "", err
	}

	if id, err := u.FindWebsiteIDByDomain(domain); err == nil && id != "" {
		return id, nil
	} else if err != nil {
		return "", err
	}

	return u.CreateWebsite(name, domain)
}

// SendEvent posts a custom event to Umami's public /api/send endpoint (no auth required).
// hostname is a label for the source (e.g., "email" or your site host).
func (u *UmamiService) SendEvent(websiteID, name, urlPath, title string, data map[string]interface{}, hostname string) error {
    if u.baseURL == "" || websiteID == "" {
        return fmt.Errorf("umami baseURL and websiteID are required")
    }
    payload := map[string]interface{}{
        "payload": map[string]interface{}{
            "hostname": hostname,
            "language": "",
            "referrer": "",
            "screen":   "",
            "title":    title,
            "url":      urlPath,
            "website":  websiteID,
            "name":     name,
            "data":     data,
        },
        "type": "event",
    }
    body, err := json.Marshal(payload)
    if err != nil {
        return fmt.Errorf("failed to marshal send event payload: %w", err)
    }
    req, err := http.NewRequest("POST", u.baseURL+"/api/send", bytes.NewBuffer(body))
    if err != nil {
        return fmt.Errorf("failed to create send event request: %w", err)
    }
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("User-Agent", "fotbal-club/server")
    client := &http.Client{Timeout: 10 * time.Second}
    resp, err := client.Do(req)
    if err != nil {
        return fmt.Errorf("failed to send umami event: %w", err)
    }
    defer resp.Body.Close()
    if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusNoContent {
        b, _ := io.ReadAll(resp.Body)
        return fmt.Errorf("umami send event status %d: %s", resp.StatusCode, string(b))
    }
    return nil
}

type UmamiAuthRequest struct {
	Username string `json:"username"`
	Password string `json:"password"`
}

type UmamiAuthResponse struct {
	Token string `json:"token"`
}

type UmamiCreateWebsiteRequest struct {
	Name   string  `json:"name"`
	Domain string  `json:"domain"`
	TeamId *string `json:"teamId,omitempty"` // Optional: team ID for Umami v2
}

type UmamiWebsiteResponse struct {
	ID        string    `json:"id"`
	Name      string    `json:"name"`
	Domain    string    `json:"domain"`
	TeamId    *string   `json:"teamId"`
	CreatedAt time.Time `json:"createdAt"`
}

// UmamiTeam represents a team in Umami
type UmamiTeam struct {
	ID   string `json:"id"`
	Name string `json:"name"`
}

// umamiTeamsResponse matches the shape of GET /api/users/:userId/teams
type umamiTeamsResponse struct {
	Data     []UmamiTeam `json:"data"`
	Count    int         `json:"count"`
	Page     int         `json:"page"`
	PageSize int         `json:"pageSize"`
}

// NewUmamiService creates a new Umami service instance
func NewUmamiService() *UmamiService {
	return &UmamiService{
		baseURL:  config.AppConfig.UmamiURL,
		username: config.AppConfig.UmamiUsername,
		password: config.AppConfig.UmamiPassword,
	}
}

// verifyToken checks if the current token is still valid using /api/auth/verify
func (u *UmamiService) verifyToken() bool {
	if u.token == "" {
		return false
	}

	req, err := http.NewRequest("POST", u.baseURL+"/api/auth/verify", nil)
	if err != nil {
		return false
	}

	req.Header.Set("Authorization", "Bearer "+u.token)
	client := &http.Client{Timeout: 5 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return false
	}
	defer resp.Body.Close()

	// Token is valid if we get 200 OK
	if resp.StatusCode == http.StatusOK {
		u.lastVerified = time.Now()
		logger.Info("Umami token verified successfully")
		return true
	}

	logger.Info("Umami token verification failed with status %d", resp.StatusCode)
	return false
}

// authenticate gets a JWT token from Umami
func (u *UmamiService) authenticate() error {
	if u.baseURL == "" || u.username == "" || u.password == "" {
		return fmt.Errorf("umami credentials not configured")
	}

	// Check if we have a token and it hasn't expired
	if u.token != "" && time.Now().Before(u.tokenExp) {
		// Verify token twice daily (every 12 hours)
		if time.Since(u.lastVerified) < 12*time.Hour {
			return nil
		}
		// Time to verify - check if token is still valid
		if u.verifyToken() {
			return nil
		}
		// Token invalid, will re-authenticate below
		logger.Info("Token expired or invalid, re-authenticating...")
	}

	authReq := UmamiAuthRequest{
		Username: u.username,
		Password: u.password,
	}

	body, err := json.Marshal(authReq)
	if err != nil {
		return fmt.Errorf("failed to marshal auth request: %w", err)
	}

	req, err := http.NewRequest("POST", u.baseURL+"/api/auth/login", bytes.NewBuffer(body))
	if err != nil {
		return fmt.Errorf("failed to create auth request: %w", err)
	}

	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{Timeout: 10 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return fmt.Errorf("failed to send auth request: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		bodyBytes, _ := io.ReadAll(resp.Body)
		return fmt.Errorf("authentication failed with status %d: %s", resp.StatusCode, string(bodyBytes))
	}

	var authResp UmamiAuthResponse
	if err := json.NewDecoder(resp.Body).Decode(&authResp); err != nil {
		return fmt.Errorf("failed to decode auth response: %w", err)
	}

	u.token = authResp.Token
	// Set token expiration to 23 hours from now (tokens typically last 24h)
	u.tokenExp = time.Now().Add(23 * time.Hour)
	u.lastVerified = time.Now()

	logger.Info("Successfully authenticated with Umami at %s (new token issued, expires at %s)", u.baseURL, u.tokenExp.Format("2006-01-02 15:04:05"))
	return nil
}

// GetUserTeams retrieves the authenticated user's teams
func (u *UmamiService) GetUserTeams(userID string) ([]UmamiTeam, error) {
	if err := u.authenticate(); err != nil {
		return nil, err
	}

	url := fmt.Sprintf("%s/api/users/%s/teams?page=1&pageSize=10", u.baseURL, userID)
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to create teams request: %w", err)
	}

	req.Header.Set("Authorization", "Bearer "+u.token)
	client := &http.Client{Timeout: 10 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("failed to send teams request: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		bodyBytes, _ := io.ReadAll(resp.Body)
		return nil, fmt.Errorf("get teams failed with status %d: %s", resp.StatusCode, string(bodyBytes))
	}

	var teamsResp umamiTeamsResponse
	if err := json.NewDecoder(resp.Body).Decode(&teamsResp); err != nil {
		return nil, fmt.Errorf("failed to decode teams response: %w", err)
	}

	return teamsResp.Data, nil
}

// GetCurrentUser retrieves the current authenticated user info from Umami
func (u *UmamiService) GetCurrentUser() (map[string]interface{}, error) {
	if err := u.authenticate(); err != nil {
		return nil, err
	}

	req, err := http.NewRequest("POST", u.baseURL+"/api/auth/verify", nil)
	if err != nil {
		return nil, fmt.Errorf("failed to create verify request: %w", err)
	}

	req.Header.Set("Authorization", "Bearer "+u.token)
	client := &http.Client{Timeout: 10 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("failed to send verify request: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		bodyBytes, _ := io.ReadAll(resp.Body)
		return nil, fmt.Errorf("verify failed with status %d: %s", resp.StatusCode, string(bodyBytes))
	}

	var user map[string]interface{}
	if err := json.NewDecoder(resp.Body).Decode(&user); err != nil {
		return nil, fmt.Errorf("failed to decode user response: %w", err)
	}

	return user, nil
}

// CreateWebsite creates a new website in Umami and returns the website ID
func (u *UmamiService) CreateWebsite(name, domain string) (string, error) {
	if err := u.authenticate(); err != nil {
		return "", err
	}

	logger.Info("Creating Umami website: name='%s', domain='%s'", name, domain)

	// Try to get user info and teams for Umami v2 compatibility
	var teamID *string
	user, err := u.GetCurrentUser()
	if err != nil {
		logger.Warn("Failed to get current user info (continuing without team): %v", err)
	} else {
		if userID, ok := user["id"].(string); ok && userID != "" {
			teams, err := u.GetUserTeams(userID)
			if err != nil {
				logger.Warn("Failed to fetch user teams (continuing without team): %v", err)
			} else if len(teams) > 0 {
				// Use the first available team
				teamID = &teams[0].ID
				logger.Info("Using team ID: %s (team name: %s)", teams[0].ID, teams[0].Name)
			}
		}
	}

	createReq := UmamiCreateWebsiteRequest{
		Name:   name,
		Domain: domain,
		TeamId: teamID,
	}

	body, err := json.Marshal(createReq)
	if err != nil {
		return "", fmt.Errorf("failed to marshal create website request: %w", err)
	}

	logger.Info("Sending website creation request to Umami API: %s/api/websites", u.baseURL)

	req, err := http.NewRequest("POST", u.baseURL+"/api/websites", bytes.NewBuffer(body))
	if err != nil {
		return "", fmt.Errorf("failed to create website request: %w", err)
	}

	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", "Bearer "+u.token)

	client := &http.Client{Timeout: 10 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return "", fmt.Errorf("failed to send create website request: %w", err)
	}
	defer resp.Body.Close()

	// Read response body for detailed error logging
	bodyBytes, _ := io.ReadAll(resp.Body)

	if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
		logger.Error("Umami website creation failed with status %d: %s", resp.StatusCode, string(bodyBytes))
		return "", fmt.Errorf("create website failed with status %d: %s", resp.StatusCode, string(bodyBytes))
	}

	var websiteResp UmamiWebsiteResponse
	if err := json.Unmarshal(bodyBytes, &websiteResp); err != nil {
		logger.Error("Failed to decode website response: %v, body: %s", err, string(bodyBytes))
		return "", fmt.Errorf("failed to decode website response: %w", err)
	}

	logger.Info("Successfully created Umami website: %s (ID: %s, Domain: %s)", name, websiteResp.ID, websiteResp.Domain)
	return websiteResp.ID, nil
}

// GetWebsiteStats retrieves analytics stats for a website
func (u *UmamiService) GetWebsiteStats(websiteID string, startAt, endAt int64) (map[string]interface{}, error) {
	if err := u.authenticate(); err != nil {
		return nil, err
	}

	url := fmt.Sprintf("%s/api/websites/%s/stats?startAt=%d&endAt=%d", u.baseURL, websiteID, startAt, endAt)
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to create stats request: %w", err)
	}

	req.Header.Set("Authorization", "Bearer "+u.token)

	client := &http.Client{Timeout: 10 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("failed to send stats request: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		bodyBytes, _ := io.ReadAll(resp.Body)
		return nil, fmt.Errorf("get stats failed with status %d: %s", resp.StatusCode, string(bodyBytes))
	}

	var stats map[string]interface{}
	if err := json.NewDecoder(resp.Body).Decode(&stats); err != nil {
		return nil, fmt.Errorf("failed to decode stats response: %w", err)
	}

	return stats, nil
}

// GetWebsiteMetrics retrieves metrics for a website
func (u *UmamiService) GetWebsiteMetrics(websiteID, type_ string, startAt, endAt int64) ([]map[string]interface{}, error) {
	if err := u.authenticate(); err != nil {
		return nil, err
	}

	url := fmt.Sprintf("%s/api/websites/%s/metrics?type=%s&startAt=%d&endAt=%d", u.baseURL, websiteID, type_, startAt, endAt)
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to create metrics request: %w", err)
	}

	req.Header.Set("Authorization", "Bearer "+u.token)

	client := &http.Client{Timeout: 10 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("failed to send metrics request: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		bodyBytes, _ := io.ReadAll(resp.Body)
		return nil, fmt.Errorf("get metrics failed with status %d: %s", resp.StatusCode, string(bodyBytes))
	}

	var metrics []map[string]interface{}
	if err := json.NewDecoder(resp.Body).Decode(&metrics); err != nil {
		return nil, fmt.Errorf("failed to decode metrics response: %w", err)
	}

	return metrics, nil
}

// GetWebsitePageviews retrieves pageviews data over time with specified unit (hour, day, month, year)
func (u *UmamiService) GetWebsitePageviews(websiteID string, startAt, endAt int64, unit string) ([]map[string]interface{}, error) {
	if err := u.authenticate(); err != nil {
		return nil, err
	}

	url := fmt.Sprintf("%s/api/websites/%s/pageviews?startAt=%d&endAt=%d&unit=%s", u.baseURL, websiteID, startAt, endAt, unit)
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to create pageviews request: %w", err)
	}

	req.Header.Set("Authorization", "Bearer "+u.token)

	client := &http.Client{Timeout: 10 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("failed to send pageviews request: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		bodyBytes, _ := io.ReadAll(resp.Body)
		return nil, fmt.Errorf("get pageviews failed with status %d: %s", resp.StatusCode, string(bodyBytes))
	}

	var pageviews []map[string]interface{}
	if err := json.NewDecoder(resp.Body).Decode(&pageviews); err != nil {
		return nil, fmt.Errorf("failed to decode pageviews response: %w", err)
	}

	return pageviews, nil
}
