package de.cannamanage.service;

import de.cannamanage.domain.entity.InfoBoardPost;
import de.cannamanage.domain.entity.PostReadStatus;
import de.cannamanage.domain.entity.TenantContext;
import de.cannamanage.domain.enums.AuditEventType;
import de.cannamanage.domain.enums.InfoBoardCategory;
import de.cannamanage.domain.enums.NotificationType;
import de.cannamanage.service.repository.InfoBoardPostRepository;
import de.cannamanage.service.repository.MemberRepository;
import de.cannamanage.service.repository.PostReadStatusRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.UUID;

/**
 * Service for Info Board (Schwarzes Brett) operations.
 */
@Service
@Transactional
public class InfoBoardService {

    private static final Logger log = LoggerFactory.getLogger(InfoBoardService.class);

    private final InfoBoardPostRepository postRepository;
    private final PostReadStatusRepository readStatusRepository;
    private final MemberRepository memberRepository;
    private final NotificationService notificationService;
    private final AuditService auditService;

    public InfoBoardService(InfoBoardPostRepository postRepository,
                           PostReadStatusRepository readStatusRepository,
                           MemberRepository memberRepository,
                           NotificationService notificationService,
                           AuditService auditService) {
        this.postRepository = postRepository;
        this.readStatusRepository = readStatusRepository;
        this.memberRepository = memberRepository;
        this.notificationService = notificationService;
        this.auditService = auditService;
    }

    /**
     * Create a new info board post and notify all club members.
     */
    public InfoBoardPost createPost(UUID clubId, String title, String content,
                                    InfoBoardCategory category, boolean isPinned, UUID authorId) {
        InfoBoardPost post = new InfoBoardPost(clubId, title, content, category, authorId);
        post.setPinned(isPinned);

        InfoBoardPost saved = postRepository.save(post);
        log.info("Info board post created: {} in club {}", saved.getId(), clubId);

        // Dispatch notification to all club members
        try {
            var members = memberRepository.findAllByClubId(clubId);
            members.forEach(member -> {
                if (member.getUserId() != null) {
                    notificationService.sendNotification(
                            member.getUserId(),
                            NotificationType.INFO_BOARD_POST,
                            title,
                            "Neuer Beitrag im Schwarzen Brett: " + title,
                            "/portal/info-board"
                    );
                }
            });
        } catch (Exception e) {
            log.warn("Failed to send info board notifications: {}", e.getMessage());
        }

        // Audit log
        auditService.log(
                AuditEventType.INFO_BOARD_POST_CREATED,
                "InfoBoardPost",
                saved.getId(),
                authorId,
                null, null,
                "Info board post created: " + title,
                "{\"category\":\"" + category.name() + "\",\"pinned\":" + isPinned + "}",
                null
        );

        return saved;
    }

    /**
     * Update an existing post.
     */
    public InfoBoardPost updatePost(UUID postId, String title, String content,
                                    InfoBoardCategory category, Boolean isPinned) {
        InfoBoardPost post = postRepository.findById(postId)
                .orElseThrow(() -> new IllegalArgumentException("Post not found: " + postId));

        if (title != null) post.setTitle(title);
        if (content != null) post.setContent(content);
        if (category != null) post.setCategory(category);
        if (isPinned != null) post.setPinned(isPinned);

        InfoBoardPost saved = postRepository.save(post);
        log.debug("Info board post updated: {}", postId);

        auditService.log(
                AuditEventType.INFO_BOARD_POST_UPDATED,
                "InfoBoardPost",
                postId,
                null, null, null,
                "Info board post updated: " + post.getTitle(),
                null, null
        );

        return saved;
    }

    /**
     * Archive a post (hides from portal members).
     */
    public InfoBoardPost archivePost(UUID postId) {
        InfoBoardPost post = postRepository.findById(postId)
                .orElseThrow(() -> new IllegalArgumentException("Post not found: " + postId));
        post.setArchived(true);
        return postRepository.save(post);
    }

    /**
     * Unarchive a post.
     */
    public InfoBoardPost unarchivePost(UUID postId) {
        InfoBoardPost post = postRepository.findById(postId)
                .orElseThrow(() -> new IllegalArgumentException("Post not found: " + postId));
        post.setArchived(false);
        return postRepository.save(post);
    }

    /**
     * Delete a post permanently.
     */
    public void deletePost(UUID postId) {
        InfoBoardPost post = postRepository.findById(postId)
                .orElseThrow(() -> new IllegalArgumentException("Post not found: " + postId));

        postRepository.delete(post);
        log.info("Info board post deleted: {}", postId);

        auditService.log(
                AuditEventType.INFO_BOARD_POST_DELETED,
                "InfoBoardPost",
                postId,
                null, null, null,
                "Info board post deleted: " + post.getTitle(),
                null, null
        );
    }

    /**
     * Get posts for a club (admin view — optionally includes archived).
     */
    @Transactional(readOnly = true)
    public Page<InfoBoardPost> getPosts(UUID clubId, InfoBoardCategory category,
                                        boolean includeArchived, int page, int size) {
        var pageable = PageRequest.of(page, size);

        if (includeArchived) {
            if (category != null) {
                return postRepository.findByClubIdAndCategoryOrderByPinnedDescCreatedAtDesc(clubId, category, pageable);
            }
            return postRepository.findByClubIdOrderByPinnedDescCreatedAtDesc(clubId, pageable);
        } else {
            if (category != null) {
                return postRepository.findByClubIdAndCategoryAndArchivedFalseOrderByPinnedDescCreatedAtDesc(clubId, category, pageable);
            }
            return postRepository.findByClubIdAndArchivedFalseOrderByPinnedDescCreatedAtDesc(clubId, pageable);
        }
    }

    /**
     * Get a single post by ID.
     */
    @Transactional(readOnly = true)
    public InfoBoardPost getPost(UUID postId) {
        return postRepository.findById(postId)
                .orElseThrow(() -> new IllegalArgumentException("Post not found: " + postId));
    }

    /**
     * Mark a post as read by a member.
     */
    public void markAsRead(UUID postId, UUID memberId) {
        if (!readStatusRepository.existsByPostIdAndMemberId(postId, memberId)) {
            readStatusRepository.save(new PostReadStatus(postId, memberId));
        }
    }

    /**
     * Get unread post count for a member in their club.
     */
    @Transactional(readOnly = true)
    public long getUnreadCount(UUID clubId, UUID memberId) {
        return postRepository.countUnreadByClubIdAndMemberId(clubId, memberId);
    }

    /**
     * Toggle pin status.
     */
    public InfoBoardPost togglePin(UUID postId) {
        InfoBoardPost post = postRepository.findById(postId)
                .orElseThrow(() -> new IllegalArgumentException("Post not found: " + postId));
        post.setPinned(!post.isPinned());
        return postRepository.save(post);
    }
}
