"""Test praw.models.subreddit."""

import socket
from json import dumps
from unittest import mock
from unittest.mock import MagicMock

import pytest
import requests
import websocket
from prawcore import BadRequest, Forbidden, NotFound, RequestException, TooLarge

from praw.const import PNG_HEADER
from praw.exceptions import (
    ClientException,
    InvalidFlairTemplateID,
    RedditAPIException,
    TooLargeMediaException,
    WebSocketException,
)
from praw.models import (
    Comment,
    InlineGif,
    InlineImage,
    InlineVideo,
    ListingGenerator,
    ModAction,
    ModmailAction,
    ModmailConversation,
    ModmailMessage,
    Redditor,
    Stylesheet,
    Submission,
    Subreddit,
    SubredditMessage,
    WikiPage,
)

from ... import IntegrationTest


class TestSubredditFilters(IntegrationTest):
    def test__iter__all(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit("all")
        filters = list(subreddit.filters)
        assert len(filters) > 0
        assert all(isinstance(x, Subreddit) for x in filters)

    def test__iter__mod(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit("mod")
        filters = list(subreddit.filters)
        assert len(filters) > 0
        assert all(isinstance(x, Subreddit) for x in filters)

    def test_add(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit("all")
        subreddit.filters.add("redditdev")

    def test_add__non_special(self, reddit):
        reddit.read_only = False
        with pytest.raises(NotFound):
            reddit.subreddit("redditdev").filters.add("redditdev")

    @pytest.mark.cassette_name("TestSubredditFilters.test_add")
    def test_add__subreddit_model(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit("all")
        subreddit.filters.add(reddit.subreddit("redditdev"))

    def test_remove(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit("mod")
        subreddit.filters.remove("redditdev")

    def test_remove__non_special(self, reddit):
        reddit.read_only = False
        with pytest.raises(NotFound):
            reddit.subreddit("redditdev").filters.remove("redditdev")

    @pytest.mark.cassette_name("TestSubredditFilters.test_remove")
    def test_remove__subreddit_model(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit("mod")
        subreddit.filters.remove(reddit.subreddit("redditdev"))


class TestSubredditFlair(IntegrationTest):
    REDDITOR = pytest.placeholders.username

    def test__call(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        mapping = subreddit.flair()
        assert len(list(mapping)) > 0
        assert all(isinstance(x["user"], Redditor) for x in mapping)

    def test__call__user_filter(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        mapping = subreddit.flair(redditor=self.REDDITOR)
        assert len(list(mapping)) == 1

    def test_configure(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        subreddit.flair.configure(
            position=None,
            self_assign=True,
            link_position=None,
            link_self_assign=True,
        )

    def test_configure__defaults(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        subreddit.flair.configure()

    def test_delete(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        subreddit.flair.delete(pytest.placeholders.username)

    def test_delete_all(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        response = subreddit.flair.delete_all()
        assert len(response) > 100
        assert all("removed" in x["status"] for x in response)

    def test_set__flair_id(self, reddit):
        reddit.read_only = False
        redditor = reddit.redditor(pytest.placeholders.username)
        flair = "11c32eee-1482-11e9-bfc0-0efc81a5e8e8"
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        subreddit.flair.set(redditor, flair_template_id=flair, text="redditor flair")

    def test_set__flair_id_default_text(self, reddit):
        reddit.read_only = False
        redditor = reddit.redditor(pytest.placeholders.username)
        flair = "11c32eee-1482-11e9-bfc0-0efc81a5e8e8"
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        subreddit.flair.set(redditor, flair_template_id=flair)

    def test_set__redditor(self, reddit):
        reddit.read_only = False
        redditor = reddit.redditor(pytest.placeholders.username)
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        subreddit.flair.set(redditor, text="redditor flair")

    def test_set__redditor_css_only(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        subreddit.flair.set(pytest.placeholders.username, css_class="some class")

    def test_set__redditor_string(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        subreddit.flair.set(
            pytest.placeholders.username, css_class="some class", text="new flair"
        )

    def test_update(self, reddit):
        reddit.read_only = False
        redditor = reddit.redditor(pytest.placeholders.username)
        flair_list = [
            redditor,
            "spez",
            {"user": "bsimpson"},
            {"user": "spladug", "flair_text": "", "flair_css_class": ""},
        ]
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        response = subreddit.flair.update(flair_list, css_class="default")
        assert all(x["ok"] for x in response)
        assert not any(x["errors"] for x in response)
        assert not any(x["warnings"] for x in response)
        assert len([x for x in response if "added" in x["status"]]) == 3
        assert len([x for x in response if "removed" in x["status"]]) == 1
        for i, name in enumerate([str(redditor), "spez", "bsimpson", "spladug"]):
            assert name in response[i]["status"]

    def test_update__comma_in_text(self, reddit):
        reddit.read_only = False
        flair_list = [
            {"user": "bsimpson"},
            {"user": "spladug", "flair_text": "a,b"},
        ]
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        response = subreddit.flair.update(flair_list, css_class="default")
        assert all(x["ok"] for x in response)

    def test_update_quotes(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        response = subreddit.flair.update(
            [reddit.user.me()], css_class="testing", text='"testing"'
        )
        assert all(x["ok"] for x in response)
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        flair = next(subreddit.flair(reddit.user.me()))
        assert flair["flair_text"] == '"testing"'
        assert flair["flair_css_class"] == "testing"


class TestSubredditFlairTemplates(IntegrationTest):
    def test__iter(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        templates = list(subreddit.flair.templates)
        assert len(templates) > 100

        for flair_template in templates:
            assert flair_template["id"]

    def test_add(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        subreddit.flair.templates.add(
            "PRAW", background_color="#ABCDEF", css_class="myCSS"
        )

    def test_clear(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        subreddit.flair.templates.clear()

    def test_delete(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        template = list(subreddit.flair.templates)[0]
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        subreddit.flair.templates.delete(template["id"])

    def test_reorder(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        original = list(subreddit.flair.templates)
        flairs = [flair["id"] for flair in subreddit.flair.templates]
        subreddit.flair.templates.reorder(list(reversed(flairs)))
        assert list(subreddit.flair.templates) == list(reversed(original))

    def test_update(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        template = list(subreddit.flair.templates)[0]
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        subreddit.flair.templates.update(
            template["id"],
            background_color="#00FFFF",
            css_class="myCSS",
            text="PRAW updated",
            text_color="dark",
        )

    def test_update_false(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        template = list(subreddit.flair.templates)[0]
        subreddit.flair.templates.update(template["id"], fetch=True, text_editable=True)
        subreddit.flair.templates.update(
            template["id"], fetch=True, text_editable=False
        )
        newtemplate = list(
            filter(
                lambda _template: _template["id"] == template["id"],
                subreddit.flair.templates,
            )
        )[0]
        assert newtemplate["text_editable"] is False

    def test_update_fetch(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        template = list(subreddit.flair.templates)[0]
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        subreddit.flair.templates.update(
            template["id"],
            background_color="#00FFFF",
            css_class="myCSS",
            fetch=True,
            text="PRAW updated",
            text_color="dark",
        )

    def test_update_fetch_no_css_class(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        template = list(subreddit.flair.templates)[0]
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        subreddit.flair.templates.update(
            template["id"],
            background_color="#00FFFF",
            fetch=True,
            text="PRAW updated",
            text_color="dark",
        )

    def test_update_fetch_no_text(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        template = list(subreddit.flair.templates)[0]
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        subreddit.flair.templates.update(
            template["id"],
            background_color="#00FFFF",
            css_class="myCSS",
            fetch=True,
            text_color="dark",
        )

    def test_update_fetch_no_text_or_css_class(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        template = list(subreddit.flair.templates)[0]
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        subreddit.flair.templates.update(
            template["id"],
            background_color="#00FFFF",
            fetch=True,
            text_color="dark",
        )

    def test_update_fetch_only(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        template = list(subreddit.flair.templates)[0]
        subreddit.flair.templates.update(template["id"], fetch=True)
        newtemplate = list(
            filter(
                lambda _template: _template["id"] == template["id"],
                subreddit.flair.templates,
            )
        )[0]
        assert newtemplate == template

    def test_update_invalid(self, reddit):
        reddit.read_only = False
        with pytest.raises(InvalidFlairTemplateID):
            subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
            subreddit.flair.templates.update(
                "fake id",
                background_color="#00FFFF",
                css_class="myCSS",
                fetch=True,
                text="PRAW updated",
                text_color="dark",
            )


class TestSubredditLinkFlairTemplates(IntegrationTest):
    def test__iter(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        templates = list(subreddit.flair.link_templates)
        assert len(templates) > 100

        for template in templates:
            assert template["id"]
            assert isinstance(template["richtext"], list)
            assert all(isinstance(item, dict) for item in template["richtext"])

    def test_add(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        subreddit.flair.link_templates.add(
            "PRAW", css_class="myCSS", text_color="light"
        )

    def test_clear(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        subreddit.flair.link_templates.clear()

    def test_reorder(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        original = list(subreddit.flair.link_templates)
        flairs = [flair["id"] for flair in subreddit.flair.link_templates]
        subreddit.flair.link_templates.reorder(list(reversed(flairs)))
        assert list(subreddit.flair.link_templates) == list(reversed(original))

    def test_user_selectable(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        templates = list(subreddit.flair.link_templates.user_selectable())
        assert len(templates) >= 2

        for template in templates:
            assert template["flair_template_id"]
            assert template["flair_text"]


class TestSubredditListings(IntegrationTest):
    def test_comments(self, reddit):
        subreddit = reddit.subreddit("askreddit")
        comments = list(subreddit.comments())
        assert len(comments) == 100

    def test_controversial(self, reddit):
        subreddit = reddit.subreddit("askreddit")
        submissions = list(subreddit.controversial())
        assert len(submissions) == 100

    def test_hot(self, reddit):
        subreddit = reddit.subreddit("askreddit")
        submissions = list(subreddit.hot())
        assert len(submissions) == 100

    def test_new(self, reddit):
        subreddit = reddit.subreddit("askreddit")
        submissions = list(subreddit.new())
        assert len(submissions) == 100

    def test_rising(self, reddit):
        subreddit = reddit.subreddit("askreddit")
        submissions = list(subreddit.rising())
        assert len(submissions) == 100

    def test_top(self, reddit):
        subreddit = reddit.subreddit("askreddit")
        submissions = list(subreddit.top())
        assert len(submissions) == 100


class TestSubredditModeration(IntegrationTest):
    def test_accept_invite__no_invite(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        with pytest.raises(RedditAPIException) as excinfo:
            subreddit.mod.accept_invite()
        assert excinfo.value.items[0].error_type == "NO_INVITE_FOUND"

    def test_edited(self, reddit):
        reddit.read_only = False
        count = 0
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        for item in subreddit.mod.edited():
            assert isinstance(item, (Comment, Submission))
            count += 1
        assert count == 100

    def test_edited__only_comments(self, reddit):
        reddit.read_only = False
        count = 0
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        for item in subreddit.mod.edited(only="comments"):
            assert isinstance(item, Comment)
            count += 1
        assert count == 100

    def test_edited__only_submissions(self, reddit):
        reddit.read_only = False
        count = 0
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        for item in subreddit.mod.edited(only="submissions"):
            assert isinstance(item, Submission)
            count += 1
        assert count > 0

    def test_log(self, reddit):
        reddit.read_only = False
        count = 0
        subreddit = reddit.subreddit("mod")
        for item in subreddit.mod.log():
            assert isinstance(item, ModAction)
            count += 1
        assert count == 100

    def test_log__filters(self, reddit):
        reddit.read_only = False
        count = 0
        subreddit = reddit.subreddit("mod")
        redditor = reddit.redditor("bboe_dev")
        for item in subreddit.mod.log(action="invitemoderator", mod=redditor):
            assert isinstance(item, ModAction)
            assert item.action == "invitemoderator"
            assert isinstance(item.mod, Redditor)
            assert item.mod == "bboe_dev"
            count += 1
        assert count > 0

    def test_modqueue(self, reddit):
        reddit.read_only = False
        count = 0
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        for item in subreddit.mod.modqueue():
            assert isinstance(item, (Comment, Submission))
            count += 1
        assert count > 0

    def test_modqueue__only_comments(self, reddit):
        reddit.read_only = False
        count = 0
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        for item in subreddit.mod.modqueue(only="comments"):
            assert isinstance(item, Comment)
            count += 1
        assert count > 0

    def test_modqueue__only_submissions(self, reddit):
        reddit.read_only = False
        count = 0
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        for item in subreddit.mod.modqueue(only="submissions"):
            assert isinstance(item, Submission)
            count += 1
        assert count > 0

    def test_notes(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        notes = list(subreddit.mod.notes.redditors("Watchful1", "watchful12", "spez"))
        assert len(notes) == 3
        assert notes[0].user.name.lower() == "watchful1"
        assert notes[1].user.name.lower() == "watchful12"
        assert notes[2] is None

    def test_notes_iterate(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        distinct_ids = set()
        count_notes = 0
        for note in subreddit.mod.notes.redditors("watchful12", limit=None):
            distinct_ids.add(note.id)
            count_notes += 1

        assert len(distinct_ids) == 359
        assert count_notes == 359

    def test_reports(self, reddit):
        reddit.read_only = False
        count = 0
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        for item in subreddit.mod.reports():
            assert isinstance(item, (Comment, Submission))
            count += 1
        assert count == 100

    def test_reports__only_comments(self, reddit):
        reddit.read_only = False
        count = 0
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        for item in subreddit.mod.reports(only="comments"):
            assert isinstance(item, Comment)
            count += 1
        assert count > 0

    def test_reports__only_submissions(self, reddit):
        reddit.read_only = False
        count = 0
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        for item in subreddit.mod.reports(only="submissions"):
            assert isinstance(item, Submission)
            count += 1
        assert count == 100

    def test_spam(self, reddit):
        reddit.read_only = False
        count = 0
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        for item in subreddit.mod.spam():
            assert isinstance(item, (Comment, Submission))
            count += 1
        assert count > 0

    def test_spam__only_comments(self, reddit):
        reddit.read_only = False
        count = 0
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        for item in subreddit.mod.spam(only="comments"):
            assert isinstance(item, Comment)
            count += 1
        assert count > 0

    def test_spam__only_submissions(self, reddit):
        reddit.read_only = False
        count = 0
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        for item in subreddit.mod.spam(only="submissions"):
            assert isinstance(item, Submission)
            count += 1
        assert count > 0

    def test_unmoderated(self, reddit):
        reddit.read_only = False
        count = 0
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        for item in subreddit.mod.unmoderated():
            assert isinstance(item, (Comment, Submission))
            count += 1
        assert count > 0

    def test_update(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        before_settings = subreddit.mod.settings()
        new_title = f"{before_settings['title']}x"
        new_title = (
            "x"
            if (len(new_title) >= 20 and "placeholder" not in new_title)
            else new_title
        )
        subreddit.mod.update(title=new_title)
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        assert subreddit.title == new_title
        after_settings = subreddit.mod.settings()

        # Ensure that nothing has changed besides what was specified.
        before_settings["title"] = new_title
        assert before_settings == after_settings


class TestSubredditModerationStreams(IntegrationTest):
    def test_edited(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        generator = subreddit.mod.stream.edited()
        for i in range(10):
            assert isinstance(next(generator), (Comment, Submission))

    def test_log(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        generator = subreddit.mod.stream.log()
        for i in range(101):
            assert isinstance(next(generator), ModAction)

    def test_modmail_conversations(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit("mod")
        generator = subreddit.mod.stream.modmail_conversations()
        for i in range(101):
            assert isinstance(next(generator), ModmailConversation)

    def test_modqueue(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        generator = subreddit.mod.stream.modqueue()
        for i in range(10):
            assert isinstance(next(generator), (Comment, Submission))

    def test_reports(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        generator = subreddit.mod.stream.reports()
        for i in range(10):
            assert isinstance(next(generator), (Comment, Submission))

    def test_spam(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        generator = subreddit.mod.stream.spam()
        for i in range(10):
            assert isinstance(next(generator), (Comment, Submission))

    def test_unmoderated(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        generator = subreddit.mod.stream.unmoderated()
        for i in range(10):
            assert isinstance(next(generator), (Comment, Submission))


class TestSubredditModmail(IntegrationTest):
    def test_bulk_read(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        for conversation in subreddit.modmail.bulk_read(state="new"):
            assert isinstance(conversation, ModmailConversation)

    def test_call(self, reddit):
        reddit.read_only = False
        conversation_id = "ik72"
        subreddit = reddit.subreddit("all")
        conversation = subreddit.modmail(conversation_id)
        assert isinstance(conversation.user, Redditor)
        for message in conversation.messages:
            assert isinstance(message, ModmailMessage)
        for action in conversation.mod_actions:
            assert isinstance(action, ModmailAction)

    def test_call__internal(self, reddit):
        reddit.read_only = False
        conversation_id = "nbhy"
        subreddit = reddit.subreddit("all")
        conversation = subreddit.modmail(conversation_id)
        for message in conversation.messages:
            assert isinstance(message, ModmailMessage)
        for action in conversation.mod_actions:
            assert isinstance(action, ModmailAction)

    def test_call__mark_read(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit("all")
        conversation = subreddit.modmail("o7wz", mark_read=True)
        assert conversation.last_unread is None

    def test_conversations(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit("all")
        conversations = subreddit.modmail.conversations()
        for conversation in conversations:
            assert isinstance(conversation, ModmailConversation)
            assert isinstance(conversation.authors[0], Redditor)

    def test_conversations__other_subreddits(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit("pics")
        conversations = subreddit.modmail.conversations(
            other_subreddits=["LifeProTips"]
        )
        assert len(set(list(conversation.owner for conversation in conversations))) > 1

    def test_conversations__params(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit("all")
        conversations = subreddit.modmail.conversations(state="mod")
        for conversation in conversations:
            assert conversation.is_internal

    def test_create(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        redditor = reddit.redditor(pytest.placeholders.username)
        conversation = subreddit.modmail.create(
            subject="Subject", body="Body", recipient=redditor
        )
        assert isinstance(conversation, ModmailConversation)

    def test_subreddits(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        for subreddit in subreddit.modmail.subreddits():
            assert isinstance(subreddit, Subreddit)

    def test_unread_count(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        assert isinstance(subreddit.modmail.unread_count(), dict)


class TestSubredditQuarantine(IntegrationTest):
    def test_opt_in(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit("ferguson")
        with pytest.raises(Forbidden):
            next(subreddit.hot())
        subreddit.quaran.opt_in()
        assert isinstance(next(subreddit.hot()), Submission)

    def test_opt_out(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit("ferguson")
        subreddit.quaran.opt_out()
        with pytest.raises(Forbidden):
            next(subreddit.hot())


class TestSubredditRelationships(IntegrationTest):
    REDDITOR = "pyapitestuser3"

    @staticmethod
    def _add_remove(base, relationship, user):
        relationship = getattr(base, relationship)
        relationship.add(user)
        relationships = list(relationship())
        assert user in relationships
        redditor = relationships[relationships.index(user)]
        assert isinstance(redditor, Redditor)
        assert hasattr(redditor, "date")
        relationship.remove(user)
        assert user not in list(relationship())

    def test_banned(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        self._add_remove(subreddit, "banned", self.REDDITOR)

    def test_banned__user_filter(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        banned = subreddit.banned(redditor="pyapitestuser3")
        assert len(list(banned)) == 1

    def test_contributor(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        self._add_remove(subreddit, "contributor", self.REDDITOR)

    def test_contributor__user_filter(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        contributor = subreddit.contributor(redditor="pyapitestuser3")
        assert len(list(contributor)) == 1

    def test_contributor_leave(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        subreddit.contributor.leave()

    def test_moderator(self, reddit):
        reddit.read_only = False
        # Moderators can only be invited.
        # As of 2016-03-18 there is no API endpoint to get the moderator
        # invite list.
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        subreddit.moderator.add(self.REDDITOR)
        assert self.REDDITOR not in subreddit.moderator()

    def test_moderator__limited_permissions(self, reddit):
        reddit.read_only = False
        # Moderators can only be invited.
        # As of 2016-03-18 there is no API endpoint to get the moderator
        # invite list.
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        subreddit.moderator.add(self.REDDITOR, permissions=["access", "wiki"])
        assert self.REDDITOR not in subreddit.moderator()

    def test_moderator__user_filter(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        moderator = subreddit.moderator(redditor="pyapitestuser3")
        assert len(moderator) == 1
        assert "mod_permissions" in moderator[0].__dict__

    def test_moderator_invite__invalid_perm(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        with pytest.raises(RedditAPIException) as excinfo:
            subreddit.moderator.invite(self.REDDITOR, permissions=["a"])
        assert excinfo.value.items[0].error_type == "INVALID_PERMISSIONS"

    def test_moderator_invite__no_perms(self, reddit):
        reddit.read_only = False
        # Moderators can only be invited.
        # As of 2016-03-18 there is no API endpoint to get the moderator
        # invite list.
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        subreddit.moderator.invite(self.REDDITOR, permissions=[])
        assert self.REDDITOR not in subreddit.moderator()

    def test_moderator_invited_moderators(self, reddit):
        reddit.read_only = False
        subreddit = reddit.subreddit(pytest.placeholders.test_subreddit)
        invited = subreddit.moderator.invited()
        assert isinstance(invited, ListingGenerator)
        for moderator in invited:
