# -*- coding: utf-8 -*-
import logging
import output
from .base import BaseBuffer
from wxUI.buffers.blueski import panels as BlueskiPanels
from sessions.blueski import compose

log = logging.getLogger("controller.buffers.blueski.user")

class UserBuffer(BaseBuffer):
    def __init__(self, *args, **kwargs):
        # We need compose_user for this buffer
        kwargs["compose_func"] = "compose_user"
        super(UserBuffer, self).__init__(*args, **kwargs)
        self.type = "user"
        self.next_cursor = None
        self.sound = "new_event.ogg"
        
    def create_buffer(self, parent, name):
        self.buffer = BlueskiPanels.UserPanel(parent, name)
        self.buffer.session = self.session

    def start_stream(self, mandatory=False, play_sound=True):
        api_method = self.kwargs.get("api_method")
        if not api_method: return 0
        
        count = self.get_max_items()
        actor = (
            self.kwargs.get("actor")
            or self.kwargs.get("did")
            or self.kwargs.get("handle")
            or self.kwargs.get("id")
        )

        try:
            if api_method in ("get_followers", "get_follows"):
                res = getattr(self.session, api_method)(actor=actor, limit=count)
            else:
                res = getattr(self.session, api_method)(limit=count)
            items = self._hydrate_profiles(res.get("items", []) or [])
            self.next_cursor = res.get("cursor")
            return self.process_items(items, play_sound)
        except Exception as e:
            log.error("Error fetching user list for %s: %s", self.name, e)
            return 0

    def get_more_items(self):
        api_method = self.kwargs.get("api_method")
        if not api_method or not self.next_cursor:
            return

        count = self.get_max_items()
        actor = (
            self.kwargs.get("actor")
            or self.kwargs.get("did")
            or self.kwargs.get("handle")
            or self.kwargs.get("id")
        )
        try:
            if api_method in ("get_followers", "get_follows"):
                res = getattr(self.session, api_method)(actor=actor, limit=count, cursor=self.next_cursor)
            else:
                res = getattr(self.session, api_method)(limit=count, cursor=self.next_cursor)
            items = self._hydrate_profiles(res.get("items", []) or [])
            self.next_cursor = res.get("cursor")
            added = self.process_items(items, play_sound=False)
            if added:
                output.speak(_(u"%s items retrieved") % (str(added)), True)
        except Exception as e:
            log.error("Error fetching more user list items for %s: %s", self.name, e)

    def _hydrate_profiles(self, items):
        if not items:
            return []

        def g(obj, key, default=None):
            if isinstance(obj, dict):
                return obj.get(key, default)
            return getattr(obj, key, default)

        def resolve_profile(obj):
            if g(obj, "handle") or g(obj, "did"):
                return obj
            for key in ("subject", "actor", "profile", "user"):
                nested = g(obj, key)
                if nested and (g(nested, "handle") or g(nested, "did")):
                    return nested
            return obj

        actors = []
        for item in items:
            profile = resolve_profile(item)
            did = g(profile, "did")
            handle = g(profile, "handle")
            if did:
                actors.append(did)
            elif handle:
                actors.append(handle)

        if not actors:
            return items

        profiles = []
        if actors and hasattr(self.session, "get_profiles"):
            try:
                res = self.session.get_profiles(actors)
                profiles = res.get("items", []) or []
            except Exception:
                profiles = []
        # If batch profiles lack counts, hydrate with detailed profiles.
        if hasattr(self.session, "get_profile"):
            def counts_missing(profile_obj):
                p1 = g(profile_obj, "followersCount") or g(profile_obj, "followers_count")
                p2 = g(profile_obj, "followsCount") or g(profile_obj, "follows_count")
                p3 = g(profile_obj, "postsCount") or g(profile_obj, "posts_count")
                if p1 is None and p2 is None and p3 is None:
                    return True
                return (p1 or 0) == 0 and (p2 or 0) == 0 and (p3 or 0) == 0

            if not profiles:
                for actor in actors:
                    try:
                        p = self.session.get_profile(actor)
                        if p:
                            profiles.append(p)
                    except Exception:
                        pass
            else:
                for idx, p in enumerate(profiles):
                    if counts_missing(p):
                        did = g(p, "did") or g(p, "handle")
                        if not did:
                            continue
                        try:
                            detailed = self.session.get_profile(did)
                            if detailed:
                                profiles[idx] = detailed
                        except Exception:
                            pass

        profile_map = {}
        for p in profiles:
            did = g(p, "did")
            handle = g(p, "handle")
            if did:
                profile_map[did] = p
            if handle and handle not in profile_map:
                profile_map[handle] = p

        def needs_replace(item, profile):
            if profile is None:
                return False
            base = resolve_profile(item)
            f1 = g(base, "followersCount") or g(base, "followers_count")
            f2 = g(base, "followsCount") or g(base, "follows_count")
            f3 = g(base, "postsCount") or g(base, "posts_count")
            p1 = g(profile, "followersCount") or g(profile, "followers_count")
            p2 = g(profile, "followsCount") or g(profile, "follows_count")
            p3 = g(profile, "postsCount") or g(profile, "posts_count")
            if f1 is None and f2 is None and f3 is None:
                return True
            if (f1 or 0) == 0 and (f2 or 0) == 0 and (f3 or 0) == 0:
                return (p1 or 0) != 0 or (p2 or 0) != 0 or (p3 or 0) != 0
            return False

        enriched = []
        for item in items:
            base = resolve_profile(item)
            did = g(base, "did")
            handle = g(base, "handle")
            profile = profile_map.get(did) or profile_map.get(handle)
            if needs_replace(item, profile):
                enriched.append(profile)
            else:
                enriched.append(item)
        return enriched

class FollowersBuffer(UserBuffer):
    def __init__(self, *args, **kwargs):
        kwargs["api_method"] = "get_followers"
        super(FollowersBuffer, self).__init__(*args, **kwargs)
        self.sound = "update_followers.ogg"

    def remove_buffer(self, force=False):
        if not force:
            from wxUI import commonMessageDialogs
            import widgetUtils
            dlg = commonMessageDialogs.remove_buffer()
            if dlg != widgetUtils.YES:
                return False
        try:
            self.session.db.pop(self.name, None)
        except Exception:
            pass
        try:
            key = self.kwargs.get("actor") or self.kwargs.get("handle") or self.kwargs.get("id")
            timelines = self.session.settings["other_buffers"].get("followers_timelines") or []
            if isinstance(timelines, str):
                timelines = [t for t in timelines.split(",") if t]
            if key in timelines:
                timelines.remove(key)
                self.session.settings["other_buffers"]["followers_timelines"] = timelines
                self.session.settings.write()
        except Exception as e:
            log.error("Error updating Bluesky followers timelines settings: %s", e)
        return True

class FollowingBuffer(UserBuffer):
    def __init__(self, *args, **kwargs):
        kwargs["api_method"] = "get_follows"
        super(FollowingBuffer, self).__init__(*args, **kwargs)
        self.sound = "update_followers.ogg"

    def remove_buffer(self, force=False):
        if not force:
            from wxUI import commonMessageDialogs
            import widgetUtils
            dlg = commonMessageDialogs.remove_buffer()
            if dlg != widgetUtils.YES:
                return False
        try:
            self.session.db.pop(self.name, None)
        except Exception:
            pass
        try:
            key = self.kwargs.get("actor") or self.kwargs.get("handle") or self.kwargs.get("id")
            timelines = self.session.settings["other_buffers"].get("following_timelines") or []
            if isinstance(timelines, str):
                timelines = [t for t in timelines.split(",") if t]
            if key in timelines:
                timelines.remove(key)
                self.session.settings["other_buffers"]["following_timelines"] = timelines
                self.session.settings.write()
        except Exception as e:
            log.error("Error updating Bluesky following timelines settings: %s", e)
        return True

class BlocksBuffer(UserBuffer):
    def __init__(self, *args, **kwargs):
        kwargs["api_method"] = "get_blocks"
        super(BlocksBuffer, self).__init__(*args, **kwargs)


class PostUserListBuffer(UserBuffer):
    def __init__(self, *args, **kwargs):
        self.post_uri = kwargs.get("post_uri")
        self.api_method = kwargs.get("api_method")
        super(PostUserListBuffer, self).__init__(*args, **kwargs)
        self.type = "post_user_list"

    def start_stream(self, mandatory=False, play_sound=True):
        if not self.api_method or not self.post_uri:
            return 0
        count = self.get_max_items()
        try:
            res = getattr(self.session, self.api_method)(self.post_uri, limit=count)
            items = res.get("items", [])
            self.next_cursor = res.get("cursor")
            return self.process_items(items, play_sound)
        except Exception as e:
            log.error("Error fetching post user list for %s: %s", self.name, e)
            return 0

    def get_more_items(self):
        if not self.api_method or not self.post_uri or not self.next_cursor:
            return
        count = self.get_max_items()
        try:
            res = getattr(self.session, self.api_method)(self.post_uri, limit=count, cursor=self.next_cursor)
            items = res.get("items", [])
            self.next_cursor = res.get("cursor")
            added = self.process_items(items, play_sound=False)
            if added:
                output.speak(_(u"%s items retrieved") % (str(added)), True)
        except Exception as e:
            log.error("Error fetching more post user list items for %s: %s", self.name, e)

    def remove_buffer(self, force=False):
        if not force:
            from wxUI import commonMessageDialogs
            import widgetUtils
            dlg = commonMessageDialogs.remove_buffer()
            if dlg != widgetUtils.YES:
                return False
        try:
            self.session.db.pop(self.name, None)
        except Exception:
            pass
        return True
