#-*- coding: utf-8 -*-
u"""

.. moduleauthor:: Martí Congost <marti.congost@whads.com>
"""
from urllib import urlopen, urlencode
from simplejson import dumps, loads
from poster.encode import multipart_encode
from poster.streaminghttp import register_openers
import urllib2
register_openers()

from cocktail.translations import (
    get_language,
    translations
)
from cocktail import schema
from woost.models import Item, Configuration
from woost.extensions.opengraph import OpenGraphExtension

facebook_locales = {
    "Catalan": 1,
    "Czech": 2,
    "Welsh": 3,
    "Danish": 4,
    "German": 5,
    "English (Upside Down)": 51,
    "English (US)": 6,
    "Spanish": 23,
    "Spanish (Spain)": 7,
    "Spanish (All)": 1002,
    "Finnish": 8,
    "French (France)": 9,
    "Hungarian": 30,
    "Italian": 10,
    "Japanese": 11,
    "Korean": 12,
    "Norwegian (bokmal)": 13,
    "Dutch": 14,
    "Polish": 15,
    "Portuguese (Brazil)": 16,
    "Portuguese (Portugal)": 31,
    "Romanian": 32,
    "Russian": 17,
    "Slovak": 33,
    "Slovenian": 34,
    "Swedish": 18,
    "Thai": 35,
    "Turkish": 19,
    "Simplified Chinese (China)": 20,
    "Traditional Chinese (Hong Kong)": 21,
    "Traditional Chinese (Taiwan)": 22,
    "Afrikaans": 36,
    "Bengali": 45,
    "Bulgarian": 37,
    "Croatian": 38,
    "English (UK)": 24,
    "French (Canada)": 44,
    "Greek": 39,
    "Hindi": 46,
    "Indonesian": 25,
    "Lithuanian": 40,
    "Malay": 41,
    "Punjabi": 47,
    "Serbian": 42,
    "Filipino": 26,
    "Tamil": 48,
    "Telugu": 49,
    "Malayalam": 50,
    "Vietnamese": 27,
    "Arabic": 28,
    "Hebrew": 29
}


class FacebookPublicationTarget(Item):

    members_order = [
        "title",
        "graph_object_id",
        "administrator_id",
        "app_id",
        "app_secret",
        "auth_token",
        "languages",
        "targeting"
    ]

    visible_from_root = False

    title = schema.String(
        required = True,
        unique = True,
        indexed = True,
        normalized_index = True,
        descriptive = True,
        spellcheck = True,
        listed_by_default = False
    )

    graph_object_id = schema.String(
        required = True
    )

    administrator_id = schema.String(
        listed_by_default = False
    )

    app_id = schema.String(
        required = True,
        listed_by_default = False
    )

    app_secret = schema.String(
        required = True,
        listed_by_default = False,
        text_search = False
    )

    auth_token = schema.String(
        editable = schema.NOT_EDITABLE,
        text_search = False,
        translate_value = lambda value, language = None, **kwargs:
            translations(
                "FacebookPublicationTarget.auth_token-%s"
                % ("conceded" if value else "pending"),
                language,
                **kwargs
            )
    )

    languages = schema.Collection(
        items = schema.String(
            enumeration = lambda ctx: Configuration.instance.languages,
            translate_value = lambda value, language = None, **kwargs:
                "" if not value else translations(value)
        )
    )

    targeting = schema.CodeBlock(
        language = "python"
    )

    def publish(self, publishable):

        if self.auth_token is None:
            raise ValueError(
                "Can't publish %s to %s: missing authorization token."
                % (publishable, self)
            )

        graph_url = "https://graph.facebook.com/%s/feed" % self.graph_object_id
        post_data = self._get_publication_parameters(publishable)
        encoded_post_data = dict(
            (k, v.encode("utf-8") if isinstance(v, unicode) else v)
            for k, v in post_data.iteritems()
        )
        response = urlopen(graph_url, urlencode(encoded_post_data))
        status = response.getcode()
        if status < 200 or status > 299:
            raise FacebookPublicationError(response.read())

    def _get_publication_parameters(self, publishable):

        og = OpenGraphExtension.instance
        og_properties = og.get_properties(publishable)

        post_data = {
            "access_token": self.auth_token,
            "name": og_properties.get("og:title") or translations(publishable),
            "link": og_properties.get("og:url")
        }

        # Disregard any default image; only show an image if the published
        # content defines one
        if "og:image" not in og_properties:
            post_data["picture"] = ""

        if self.targeting:
            context = {
                "language": get_language(),
                "og_properties": og_properties,
                "publishable": publishable,
                "locales": facebook_locales,
                "include_locales": (
                    lambda included:
                        [facebook_locales[loc_name] for loc_name in included]
                ),
                "exclude_locales": (
                    lambda excluded:
                        [loc_id
                        for loc_name, loc_id in facebook_locales.iteritems()
                        if loc_name not in excluded]
                )
            }
            targeting_code = self.targeting.replace("\r\n", "\n")
            exec targeting_code in context
            targeting = context.get("targeting")
            if targeting:
                post_data["feed_targeting"] = dumps(targeting)

        return post_data

    def feed_posts(self):

        if self.auth_token is None:
            raise ValueError(
                "Can't read the posts in %s: missing authorization token."
                % self
            )

        graph_url = "https://graph.facebook.com/%s/feed/?access_token=%s" % (
            self.graph_object_id,
            self.auth_token
        )
        response = urlopen(graph_url)
        status = response.getcode()
        body = response.read()
        if status < 200 or status > 299:
            raise FacebookPublicationError(body)

        feed_data = loads(body)
        return feed_data["data"]

    def find_post(self, publishable):

        uri = self._get_publication_parameters(publishable)["link"]

        for post in self.feed_posts():
            if post.get("link") == uri:
                return post

    def publish_album(self,
        album_title,
        photos,
        album_description = None,
        photo_languages = None,
        generate_story = True):

        if self.auth_token is None:
            raise ValueError(
                "Can't publish album to %s: missing authorization token."
                % self
            )

        # Create the album
        response = urlopen(
            "https://graph.facebook.com/%s/albums" % self.graph_object_id,
            _encode_form({
                "access_token": self.auth_token,
                "name": album_title,
                "message": album_description or ""
            })
        )

        status = response.getcode()
        body = response.read()
        if status < 200 or status > 299:
            raise FacebookPublicationError(body)

        album_id = loads(body)["id"]
        photos_url = "https://graph.facebook.com/%s/photos" % album_id

        # Upload photos
        for photo in photos:

            if photo_languages is None:
                photo_languages = photo.translations.keys()
            else:
                photo_languages = list(
                    set(photo_languages) & set(photo.translations.keys())
                )

            if not photo_languages:
                photo_desc = ""
            elif len(photo_languages) == 1:
                photo_desc = translations(photo, photo_languages[0])
            else:
                photo_desc = u"\n".join(
                    u"%s: %s" % (
                        translations(lang, lang),
                        translations(photo, lang)
                    )
                    for lang in photo_languages
                )

            datagen, headers = multipart_encode({
                "access_token": self.auth_token,
                "source": open(photo.file_path),
                "message": photo_desc,
                "no_story": ("0" if generate_story else "1")
            })
            request = urllib2.Request(photos_url, datagen, headers)
            response = urllib2.urlopen(request)

            status = response.getcode()
            body = response.read()
            if status < 200 or status > 299:
                raise FacebookPublicationError(body)

        response = urlopen(
            "https://graph.facebook.com/%s/?access_token=%s" % (
                album_id,
                self.auth_token
            )
        )
        status = response.getcode()
        body = response.read()
        if status < 200 or status > 299:
            raise FacebookPublicationError(body)

        return loads(body)

def _encode_form(form):
    return urlencode(dict(
        (k, v.encode("utf-8") if isinstance(v, unicode) else v)
        for k, v in form.iteritems()
    ))


class FacebookPublicationError(Exception):

    def __init__(self, response):
        Exception.__init__(self, response)
        self.response = response

