package calendar

import (
	"context"
	"errors"
	"fmt"
	"strings"
	"time"

	"github.com/ultisuite/ulti-backend/internal/api/paginate"
	"github.com/ultisuite/ulti-backend/internal/api/query"
	meetpkg "github.com/ultisuite/ulti-backend/internal/meet"
	"github.com/ultisuite/ulti-backend/internal/nextcloud"
)

type Service struct {
	nc      *nextcloud.Client
	meetCfg *meetpkg.Config
}

var ErrMeetDisabled = errors.New("meet is disabled")

func NewService(nc *nextcloud.Client, meetCfg *meetpkg.Config) *Service {
	return &Service{nc: nc, meetCfg: meetCfg}
}

func calendarPath(userID, calID string) string {
	return "/remote.php/dav/calendars/" + userID + "/" + calID + "/"
}

func (s *Service) ListCalendars(ctx context.Context, userID string) ([]nextcloud.Calendar, error) {
	return s.nc.ListCalendars(ctx, userID)
}

type EventsList struct {
	Events     []nextcloud.Event    `json:"events"`
	Pagination query.PaginationMeta `json:"pagination,omitempty"`
}

func (s *Service) ListEvents(ctx context.Context, userID, calID string, params query.ListParams) (EventsList, error) {
	from := time.Now().AddDate(0, -1, 0)
	to := time.Now().AddDate(0, 1, 0)
	if params.From != nil {
		from = *params.From
	}
	if params.To != nil {
		to = *params.To
	}

	events, err := s.nc.ListEvents(ctx, userID, calendarPath(userID, calID), from, to)
	if err != nil {
		return EventsList{}, err
	}
	filtered := filterEvents(events, params.Q)
	page, total := paginate.Slice(filtered, params.Offset(), params.Limit())
	return EventsList{
		Events:     page,
		Pagination: params.Meta(&total),
	}, nil
}

func (s *Service) CreateEvent(ctx context.Context, userID, calID string, event *nextcloud.Event) error {
	if strings.TrimSpace(event.Organizer) == "" {
		event.Organizer = userID
	}
	return s.nc.CreateEvent(ctx, userID, calendarPath(userID, calID), event)
}

func (s *Service) UpdateEvent(ctx context.Context, userID, eventPath, ifMatch string, event *nextcloud.Event) (string, error) {
	if strings.TrimSpace(event.Organizer) == "" {
		event.Organizer = userID
	}
	return s.nc.UpdateEvent(ctx, userID, eventPath, ifMatch, event)
}

func (s *Service) DeleteEvent(ctx context.Context, userID, eventPath string) error {
	return s.nc.DeleteEvent(ctx, userID, eventPath)
}

func (s *Service) FreeBusy(ctx context.Context, userID string, req *nextcloud.FreeBusyRequest) (*nextcloud.FreeBusyResponse, error) {
	return s.nc.FreeBusy(ctx, userID, req)
}

func (s *Service) RespondToInvitation(ctx context.Context, userID, eventPath, attendeeEmail, status, ifMatch string) (string, error) {
	event, err := s.nc.GetEvent(ctx, userID, eventPath)
	if err != nil {
		return "", err
	}
	target := strings.ToLower(strings.TrimSpace(attendeeEmail))
	for i := range event.Attendees {
		if strings.EqualFold(strings.TrimSpace(event.Attendees[i].Email), target) {
			event.Attendees[i].Status = strings.ToUpper(strings.TrimSpace(status))
			match := strings.TrimSpace(ifMatch)
			if match == "" {
				match = strings.TrimSpace(event.ETag)
			}
			return s.nc.UpdateEvent(ctx, userID, eventPath, match, event)
		}
	}
	return "", nextcloud.ErrAttendeeNotFound
}

func (s *Service) CreateMeetLink(ctx context.Context, userID, userName, userEmail, eventPath, ifMatch string) (string, string, error) {
	if s.meetCfg == nil {
		return "", "", ErrMeetDisabled
	}
	event, err := s.nc.GetEvent(ctx, userID, eventPath)
	if err != nil {
		return "", "", err
	}
	roomID := strings.TrimSpace(event.UID)
	if roomID == "" {
		roomID = fmt.Sprintf("event-%d", time.Now().Unix())
	}
	token, err := s.meetCfg.GenerateToken(roomID, &meetpkg.UserInfo{
		ID:    userID,
		Name:  userName,
		Email: userEmail,
		IsMod: true,
	}, 24*time.Hour)
	if err != nil {
		return "", "", err
	}
	event.MeetURL = token.MeetURL
	if strings.TrimSpace(event.Location) == "" {
		event.Location = token.MeetURL
	}
	match := strings.TrimSpace(ifMatch)
	if match == "" {
		match = strings.TrimSpace(event.ETag)
	}
	etag, err := s.nc.UpdateEvent(ctx, userID, eventPath, match, event)
	if err != nil {
		return "", "", err
	}
	return token.MeetURL, etag, nil
}

func filterEvents(events []nextcloud.Event, q string) []nextcloud.Event {
	q = strings.ToLower(strings.TrimSpace(q))
	if q == "" {
		return events
	}
	out := make([]nextcloud.Event, 0, len(events))
	for _, e := range events {
		if strings.Contains(strings.ToLower(e.Summary), q) ||
			strings.Contains(strings.ToLower(e.Description), q) ||
			strings.Contains(strings.ToLower(e.Location), q) {
			out = append(out, e)
		}
	}
	return out
}
