// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package processor // import "miniflux.app/v2/internal/reader/processor"

import (
	"encoding/json"
	"errors"
	"fmt"
	"log/slog"
	"regexp"
	"strings"

	"miniflux.app/v2/internal/config"
	"miniflux.app/v2/internal/model"
	"miniflux.app/v2/internal/proxyrotator"
	"miniflux.app/v2/internal/reader/fetcher"
)

var (
	bilibiliVideoIdRegex = regexp.MustCompile(`/video/(?:av(\d+)|BV([a-zA-Z0-9]+))`)
)

func shouldFetchBilibiliWatchTime(entry *model.Entry) bool {
	if !config.Opts.FetchBilibiliWatchTime() {
		return false
	}
	return strings.Contains(entry.URL, "bilibili.com/video/")
}

func extractBilibiliVideoID(websiteURL string) (string, string, error) {
	matches := bilibiliVideoIdRegex.FindStringSubmatch(websiteURL)
	if matches == nil {
		return "", "", fmt.Errorf("no video ID found in URL: %s", websiteURL)
	}
	if matches[1] != "" {
		return "aid", matches[1], nil
	}
	if matches[2] != "" {
		return "bvid", matches[2], nil
	}
	return "", "", fmt.Errorf("unexpected regex match result for URL: %s", websiteURL)
}

func fetchBilibiliWatchTime(websiteURL string) (int, error) {
	requestBuilder := fetcher.NewRequestBuilder().
		WithTimeout(config.Opts.HTTPClientTimeout()).
		WithProxyRotator(proxyrotator.ProxyRotatorInstance)

	idType, videoID, extractErr := extractBilibiliVideoID(websiteURL)
	if extractErr != nil {
		return 0, extractErr
	}
	bilibiliApiURL := "https://api.bilibili.com/x/web-interface/view?" + idType + "=" + videoID

	responseHandler := fetcher.NewResponseHandler(requestBuilder.ExecuteRequest(bilibiliApiURL))
	defer responseHandler.Close()

	if localizedError := responseHandler.LocalizedError(); localizedError != nil {
		slog.Warn("Unable to fetch Bilibili API",
			slog.String("website_url", websiteURL),
			slog.String("api_url", bilibiliApiURL),
			slog.Any("error", localizedError.Error()))
		return 0, localizedError.Error()
	}

	var result map[string]any
	doc := json.NewDecoder(responseHandler.Body(config.Opts.HTTPClientMaxBodySize()))
	if docErr := doc.Decode(&result); docErr != nil {
		return 0, fmt.Errorf("failed to decode API response: %v", docErr)
	}

	if code, ok := result["code"].(float64); !ok || code != 0 {
		return 0, fmt.Errorf("API returned error code: %v", result["code"])
	}

	data, ok := result["data"].(map[string]any)
	if !ok {
		return 0, errors.New("data field not found or not an object")
	}

	duration, ok := data["duration"].(float64)
	if !ok {
		return 0, errors.New("duration not found or not a number")
	}
	intDuration := int(duration)
	durationMin := intDuration / 60
	if intDuration%60 != 0 {
		durationMin++
	}
	return durationMin, nil
}
