import warnings
from itertools import product
from unittest import TestCase

from scrapy.downloadermiddlewares.stats import DownloaderStats
from scrapy.exceptions import ScrapyDeprecationWarning
from scrapy.http import Request, Response
from scrapy.spiders import Spider
from scrapy.utils.response import response_httprepr
from scrapy.utils.test import get_crawler


class MyException(Exception):
    pass


class TestDownloaderStats(TestCase):
    def setUp(self):
        self.crawler = get_crawler(Spider)
        self.spider = self.crawler._create_spider("scrapytest.org")
        self.mw = DownloaderStats(self.crawler.stats)

        self.crawler.stats.open_spider(self.spider)

        self.req = Request("http://scrapytest.org")
        self.res = Response("scrapytest.org", status=400)

    def assertStatsEqual(self, key, value):
        self.assertEqual(
            self.crawler.stats.get_value(key, spider=self.spider),
            value,
            str(self.crawler.stats.get_stats(self.spider)),
        )

    def test_process_request(self):
        self.mw.process_request(self.req, self.spider)
        self.assertStatsEqual("downloader/request_count", 1)

    def test_process_response(self):
        self.mw.process_response(self.req, self.res, self.spider)
        self.assertStatsEqual("downloader/response_count", 1)

    def test_response_len(self):
        body = (b"", b"not_empty")  # empty/notempty body
        headers = (
            {},
            {"lang": "en"},
            {"lang": "en", "User-Agent": "scrapy"},
        )  # 0 headers, 1h and 2h
        test_responses = [  # form test responses with all combinations of body/headers
            Response(url="scrapytest.org", status=200, body=r[0], headers=r[1])
            for r in product(body, headers)
        ]
        for test_response in test_responses:
            self.crawler.stats.set_value("downloader/response_bytes", 0)
            self.mw.process_response(self.req, test_response, self.spider)
            with warnings.catch_warnings():
                warnings.simplefilter("ignore", ScrapyDeprecationWarning)
                resp_size = len(response_httprepr(test_response))
            self.assertStatsEqual("downloader/response_bytes", resp_size)

    def test_process_exception(self):
        self.mw.process_exception(self.req, MyException(), self.spider)
        self.assertStatsEqual("downloader/exception_count", 1)
        self.assertStatsEqual(
            "downloader/exception_type_count/tests.test_downloadermiddleware_stats.MyException",
            1,
        )

    def tearDown(self):
        self.crawler.stats.close_spider(self.spider, "")
