package vimeo

import (
	"Yimaru-Backend/internal/pkgs/vimeo"
	"context"
	"fmt"
	"io"

	"go.uber.org/zap"
)

type Service struct {
	client *vimeo.Client
	logger *zap.Logger
}

func NewService(accessToken string, logger *zap.Logger) *Service {
	return &Service{
		client: vimeo.NewClient(accessToken),
		logger: logger,
	}
}

type VideoInfo struct {
	VimeoID         string
	URI             string
	Name            string
	Description     string
	Duration        int
	Width           int
	Height          int
	Link            string
	EmbedURL        string
	EmbedHTML       string
	ThumbnailURL    string
	Status          string
	TranscodeStatus string
}

type UploadResult struct {
	VimeoID    string
	URI        string
	Link       string
	UploadLink string
	Status     string
}

func (s *Service) GetVideoInfo(ctx context.Context, videoID string) (*VideoInfo, error) {
	video, err := s.client.GetVideo(ctx, videoID)
	if err != nil {
		s.logger.Error("Failed to get video from Vimeo", zap.String("video_id", videoID), zap.Error(err))
		return nil, fmt.Errorf("failed to get video: %w", err)
	}

	info := &VideoInfo{
		VimeoID:     videoID,
		URI:         video.URI,
		Name:        video.Name,
		Description: video.Description,
		Duration:    video.Duration,
		Width:       video.Width,
		Height:      video.Height,
		Link:        video.Link,
		Status:      video.Status,
	}

	if video.PlayerEmbedURL != "" {
		info.EmbedURL = video.PlayerEmbedURL
	} else {
		info.EmbedURL = vimeo.GenerateEmbedURL(videoID, nil)
	}

	if video.Embed != nil {
		info.EmbedHTML = video.Embed.HTML
	}

	if video.Pictures != nil && len(video.Pictures.Sizes) > 0 {
		info.ThumbnailURL = video.Pictures.Sizes[len(video.Pictures.Sizes)-1].Link
	}

	if video.Transcode != nil {
		info.TranscodeStatus = video.Transcode.Status
	}

	return info, nil
}

func (s *Service) CreatePullUpload(ctx context.Context, name, description, sourceURL string, fileSize int64) (*UploadResult, error) {
	resp, err := s.client.CreatePullUpload(ctx, name, description, sourceURL, fileSize)
	if err != nil {
		s.logger.Error("Failed to create pull upload", zap.String("source_url", sourceURL), zap.Error(err))
		return nil, fmt.Errorf("failed to create pull upload: %w", err)
	}

	videoID := vimeo.ExtractVideoID(resp.URI)

	return &UploadResult{
		VimeoID:    videoID,
		URI:        resp.URI,
		Link:       resp.Link,
		UploadLink: resp.Upload.UploadLink,
		Status:     resp.Upload.Status,
	}, nil
}

func (s *Service) CreateTusUpload(ctx context.Context, name, description string, fileSize int64) (*UploadResult, error) {
	resp, err := s.client.CreateTusUpload(ctx, name, description, fileSize)
	if err != nil {
		s.logger.Error("Failed to create TUS upload", zap.Error(err))
		return nil, fmt.Errorf("failed to create TUS upload: %w", err)
	}

	videoID := vimeo.ExtractVideoID(resp.URI)

	return &UploadResult{
		VimeoID:    videoID,
		URI:        resp.URI,
		Link:       resp.Link,
		UploadLink: resp.Upload.UploadLink,
		Status:     resp.Upload.Status,
	}, nil
}

func (s *Service) UploadVideoFile(ctx context.Context, name, description string, fileData io.Reader, fileSize int64) (*UploadResult, error) {
	resp, err := s.client.CreateTusUpload(ctx, name, description, fileSize)
	if err != nil {
		s.logger.Error("Failed to create TUS upload slot", zap.Error(err))
		return nil, fmt.Errorf("failed to create TUS upload slot: %w", err)
	}

	if err := s.client.UploadTusVideoFile(ctx, resp.Upload.UploadLink, fileData, fileSize); err != nil {
		s.logger.Error("Failed to upload video file to Vimeo", zap.Error(err))
		return nil, fmt.Errorf("failed to upload video file: %w", err)
	}

	videoID := vimeo.ExtractVideoID(resp.URI)

	return &UploadResult{
		VimeoID:    videoID,
		URI:        resp.URI,
		Link:       resp.Link,
		UploadLink: resp.Upload.UploadLink,
		Status:     "uploading",
	}, nil
}

func (s *Service) UpdateVideoMetadata(ctx context.Context, videoID string, name, description *string) (*VideoInfo, error) {
	req := &vimeo.UpdateVideoRequest{
		Name:        name,
		Description: description,
	}

	video, err := s.client.UpdateVideo(ctx, videoID, req)
	if err != nil {
		s.logger.Error("Failed to update video metadata", zap.String("video_id", videoID), zap.Error(err))
		return nil, fmt.Errorf("failed to update video: %w", err)
	}

	info := &VideoInfo{
		VimeoID:     videoID,
		URI:         video.URI,
		Name:        video.Name,
		Description: video.Description,
		Duration:    video.Duration,
		Link:        video.Link,
	}

	return info, nil
}

func (s *Service) DeleteVideo(ctx context.Context, videoID string) error {
	if err := s.client.DeleteVideo(ctx, videoID); err != nil {
		s.logger.Error("Failed to delete video from Vimeo", zap.String("video_id", videoID), zap.Error(err))
		return fmt.Errorf("failed to delete video: %w", err)
	}
	return nil
}

func (s *Service) GetTranscodeStatus(ctx context.Context, videoID string) (string, error) {
	status, err := s.client.GetTranscodeStatus(ctx, videoID)
	if err != nil {
		return "", fmt.Errorf("failed to get transcode status: %w", err)
	}
	return status, nil
}

func (s *Service) GetEmbedCode(ctx context.Context, videoID string, width, height int, opts *vimeo.EmbedOptions) (string, error) {
	return vimeo.GenerateIframeEmbed(videoID, width, height, opts), nil
}

func (s *Service) GetOEmbed(ctx context.Context, vimeoURL string, width, height int) (*vimeo.OEmbedResponse, error) {
	return vimeo.GetOEmbed(ctx, vimeoURL, width, height)
}

func (s *Service) GeneratePlayerURL(videoID string, opts *vimeo.EmbedOptions) string {
	return vimeo.GenerateEmbedURL(videoID, opts)
}
