package catalog

import (
	"context"
	"database/sql"
	"fmt"
	"net/http"
	"strings"

	"gorm.io/gorm"
)

type ProductPageRequest struct {
	ID         int64
	Slug       string
	LanguageID int64
	ShopID     int64
}

type CategoryPageRequest struct {
	ID         int64
	Slug       string
	LanguageID int64
	ShopID     int64
}

type ProductPageData struct {
	ID               int64
	Name             string
	Slug             string
	ShortDescription string
	Description      string
	Price            float64
	CoverImageID     sql.NullInt64
	CategoryID       int64
	CategorySlug     string
	CategoryName     string
}

type CategoryPageData struct {
	ID          int64
	Name        string
	Slug        string
	Description string
	Products    []CategoryProductCard `gorm:"-"`
}

type CategoryProductCard struct {
	ID          int64
	Name        string
	Slug        string
	URL         string `gorm:"-"`
	Price       float64
	Description string
	EAN13       string
}

type Service struct {
	db     *gorm.DB
	prefix string
}

func NewService(db *gorm.DB, prefix string) *Service {
	return &Service{db: db, prefix: prefix}
}

func (s *Service) GetProductPage(ctx context.Context, req ProductPageRequest) (*ProductPageData, error) {
	var product ProductPageData
	queryByID := fmt.Sprintf(`
SELECT p.id_product AS id,
       pl.name AS name,
       pl.link_rewrite AS slug,
       pl.description_short AS short_description,
       pl.description AS description,
       ps.price AS price,
       i.id_image AS cover_image_id,
       p.id_category_default AS category_id,
       cl.link_rewrite AS category_slug,
       cl.name AS category_name
FROM %sproduct p
JOIN %sproduct_lang pl ON pl.id_product = p.id_product
JOIN %sproduct_shop ps ON ps.id_product = p.id_product
LEFT JOIN %scategory_lang cl ON cl.id_category = p.id_category_default AND cl.id_lang = pl.id_lang
LEFT JOIN %simage i ON i.id_product = p.id_product AND i.cover = 1
WHERE p.id_product = ?
  AND pl.id_lang = ?
  AND ps.id_shop = ?
LIMIT 1
`, s.prefix, s.prefix, s.prefix, s.prefix, s.prefix)
	queryBySlug := fmt.Sprintf(`
SELECT p.id_product AS id,
       pl.name AS name,
       pl.link_rewrite AS slug,
       pl.description_short AS short_description,
       pl.description AS description,
       ps.price AS price,
       i.id_image AS cover_image_id,
       p.id_category_default AS category_id,
       cl.link_rewrite AS category_slug,
       cl.name AS category_name
FROM %sproduct p
JOIN %sproduct_lang pl ON pl.id_product = p.id_product
JOIN %sproduct_shop ps ON ps.id_product = p.id_product
LEFT JOIN %scategory_lang cl ON cl.id_category = p.id_category_default AND cl.id_lang = pl.id_lang
LEFT JOIN %simage i ON i.id_product = p.id_product AND i.cover = 1
WHERE pl.link_rewrite = ?
  AND pl.id_lang = ?
  AND ps.id_shop = ?
LIMIT 1
`, s.prefix, s.prefix, s.prefix, s.prefix, s.prefix)

	var result *gorm.DB
	if req.ID != 0 {
		result = s.db.WithContext(ctx).Raw(strings.TrimSpace(queryByID), req.ID, req.LanguageID, req.ShopID).Scan(&product)
	} else {
		result = s.db.WithContext(ctx).Raw(strings.TrimSpace(queryBySlug), req.Slug, req.LanguageID, req.ShopID).Scan(&product)
	}
	if result.Error != nil {
		return nil, result.Error
	}
	if result.RowsAffected == 0 {
		return nil, gorm.ErrRecordNotFound
	}

	return &product, nil
}

func (s *Service) GetCategoryPage(ctx context.Context, req CategoryPageRequest) (*CategoryPageData, error) {
	var category CategoryPageData
	categoryQuery := fmt.Sprintf(`
SELECT c.id_category AS id,
       cl.name AS name,
       cl.link_rewrite AS slug,
       cl.description AS description
FROM %scategory c
JOIN %scategory_lang cl ON cl.id_category = c.id_category
WHERE c.id_category = ?
  AND cl.id_lang = ?
LIMIT 1
`, s.prefix, s.prefix)
	categoryFallbackQuery := fmt.Sprintf(`
SELECT c.id_category AS id,
       cl.name AS name,
       cl.link_rewrite AS slug,
       cl.description AS description
FROM %scategory c
JOIN %scategory_lang cl ON cl.id_category = c.id_category
WHERE c.id_category = ?
ORDER BY cl.id_lang ASC
LIMIT 1
`, s.prefix, s.prefix)

	lookupID := req.ID
	if lookupID == 0 {
		idQuery := fmt.Sprintf(`
SELECT c.id_category
FROM %scategory c
JOIN %scategory_lang cl ON cl.id_category = c.id_category
WHERE cl.link_rewrite = ?
  AND cl.id_lang = ?
LIMIT 1
`, s.prefix, s.prefix)
		var row struct {
			ID int64 `gorm:"column:id_category"`
		}
		result := s.db.WithContext(ctx).Raw(strings.TrimSpace(idQuery), req.Slug, req.LanguageID).Scan(&row)
		if result.Error != nil {
			return nil, result.Error
		}
		if result.RowsAffected == 0 {
			return nil, gorm.ErrRecordNotFound
		}
		lookupID = row.ID
	}

	result := s.db.WithContext(ctx).Raw(strings.TrimSpace(categoryQuery), lookupID, req.LanguageID).Scan(&category)
	if result.Error != nil {
		return nil, result.Error
	}
	if result.RowsAffected == 0 {
		result = s.db.WithContext(ctx).Raw(strings.TrimSpace(categoryFallbackQuery), lookupID).Scan(&category)
		if result.Error != nil {
			return nil, result.Error
		}
		if result.RowsAffected == 0 {
			return nil, gorm.ErrRecordNotFound
		}
	}

	productQuery := fmt.Sprintf(`
SELECT p.id_product AS id,
       pl.name AS name,
       pl.link_rewrite AS slug,
       p.ean13 AS ean13,
       ps.price AS price,
       pl.description_short AS description
FROM %scategory_product cp
JOIN %sproduct p ON p.id_product = cp.id_product
JOIN %sproduct_lang pl ON pl.id_product = p.id_product
JOIN %sproduct_shop ps ON ps.id_product = p.id_product
WHERE cp.id_category = ?
  AND pl.id_lang = ?
  AND ps.id_shop = ?
ORDER BY cp.position ASC, p.id_product ASC
LIMIT 48
`, s.prefix, s.prefix, s.prefix, s.prefix)

	if err := s.db.WithContext(ctx).Raw(strings.TrimSpace(productQuery), category.ID, req.LanguageID, req.ShopID).Scan(&category.Products).Error; err != nil {
		return nil, err
	}

	return &category, nil
}

func (s *Service) ResolveLanguageID(ctx context.Context, req *http.Request, fallback int64) int64 {
	if req == nil || req.URL == nil {
		return fallback
	}
	path := strings.Trim(req.URL.Path, "/")
	if path == "" {
		return fallback
	}
	first := path
	if idx := strings.IndexByte(path, '/'); idx >= 0 {
		first = path[:idx]
	}
	first = strings.TrimSpace(first)
	if len(first) < 2 || len(first) > 5 {
		return fallback
	}

	var row struct {
		ID int64 `gorm:"column:id_lang"`
	}
	query := fmt.Sprintf("SELECT id_lang FROM %slang WHERE iso_code = ? LIMIT 1", s.prefix)
	result := s.db.WithContext(ctx).Raw(query, strings.ToUpper(first)).Scan(&row)
	if result.Error != nil || result.RowsAffected == 0 || row.ID == 0 {
		return fallback
	}
	return row.ID
}
