# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import sys
from yt.wrapper import TablePath

from datacloud.data_schema.path_collection import PathCollection, BaseTable


audience_yt_schema = [
    {"name": "external_id", "type": "string"},
    {"name": "impulses", "type": "any"},
    {"name": "phones", "type": "any"},
    {"name": "emails", "type": "any"},
    {"name": "cookies", "type": "any"},
    {"name": "birth_date", "type": "any"},
    {"name": "gender", "type": "string"},
    {"name": "ban_info", "type": "any"},
    {"name": "add_date", "type": "string"},
    {"name": "yandex_can_use", "type": "boolean"},
    {"name": "other_fields", "type": "any"},
    {"name": "impulse_fields", "type": "any"},
]


class PartnerAudienceTables(PathCollection):
    def __init__(
            self,
            partner,
            base_root="//home/x-products/production",
            sub_folder="/partners_data/audience",
            cookies_root=None,
            yt_cluster="hahn",
            **kwargs
    ):
        super(PartnerAudienceTables, self).__init__(
            yt_cluster=yt_cluster,
            **kwargs
        )
        self.root = base_root + sub_folder

        self.audience_table = BaseTable(
            path=self.root + "/" + partner,
            schema=audience_yt_schema,
            yt_client=self.yt_client
        )
        self.updates_table = BaseTable(
            path=self.root + "/" + partner + ".updates",
            schema=audience_yt_schema,
            yt_client=self.yt_client
        )
        self._merge_table = BaseTable(
            path=self.root + "/" + partner + ".merge_in_progress",
            schema=audience_yt_schema,
            yt_client=self.yt_client
        )
        self._merge_result = BaseTable(
            path=self.root + "/" + partner + ".merge_result",
            schema=audience_yt_schema,
            yt_client=self.yt_client
        )

    def create_tables(self):
        self.yt_client.mkdir(self.root, recursive=True)
        self.audience_table.create_table()
        self.updates_table.create_table()

    def write_updates(self, recs_generator):
        self.create_tables()
        self.updates_table.ensure_schema_is_valid()
        self.yt_client.write_table(
            TablePath(self.updates_table, append=True),
            validate_generator(recs_generator)
        )

    def merge_update(self):
        if self.yt_client.get_attribute(self.updates_table, "row_count") == 0:
            return
            # TODO add lock with lock menager
        if not self.yt_client.exists(self._merge_table):
            with self.yt_client.Transaction():
                self.yt_client.move(
                    self.updates_table,
                    self._merge_table
                )
                self.updates_table.create_table()
        self.yt_client.run_sort(
            self._merge_table,
            sort_by=("external_id", "add_date")
        )
        self.audience_table.create_table()
        self.yt_client.run_sort(
            self.audience_table,
            sort_by=("external_id", "add_date")
        )

        self._merge_result.create_table()
        self.yt_client.run_reduce(
            merge_call,
            [
                self.audience_table,
                self._merge_table,
            ],
            self._merge_result,
            reduce_by="external_id"
        )
        with self.yt_client.Transaction():
            self.yt_client.remove(self.audience_table)
            self.yt_client.move(
                self._merge_result,
                self.audience_table
            )
            self.yt_client.remove(self._merge_table)


audience_json_schema = {
    "description": "Запись про одного человека в аудиториях",
    "id": "external_id_info",
    "properties": {
        "impulses": {
            "type": "array",
            "items": {
                "type": "object",
                "properties": {
                    "impulse": {
                        "type": "string"
                    },
                   "delete": {
                        "type": "string",
                        "format": "date-time",
                    },
                   "ban_untill": {
                        "type": "string",
                        "format": "date-time",
                    }
                }
            }
        },
        "external_id": {
            "type": "string"
        },
        "phones": {
            "type": "array",
            "items": {
                "type": "object",
                "properties": {
                    "id_value": {
                        "type": "string"
                    },
                    "phone": {
                        "type": "string"
                    },
                    "delete": {
                        "type": "string",
                        "format": "date-time",
                    }
                },
                "required": [
                    "id_value",
                ]
            }
        },
        "emails": {
            "type": "array",
            "items": {
                "type": "object",
                "properties": {
                    "id_value": {
                        "type": "string"
                    },
                    "email": {
                        "type": "string"
                    },
                    "delete": {
                        "type": "string",
                        "format": "date-time",
                    }
                },
                "required": [
                    "id_value",
                ]
            }
        },
        "cookies": {
            "type": "array",
            "items": {
                "type": "object",
                "properties": {
                    "cookie": {
                        "type": "string"
                    },
                    "cookie_vendor": {
                        "type": "string"
                    },
                    "cookie_name": {
                        "type": "string"
                    },
                    "sync_from": {
                        "type": "object",
                        "properties": {
                            "sync_dates": {
                                "type": "array",
                                "items": {
                                    "type": "string",
                                    "format": "date-time",
                                }
                            },
                            "cookie": {
                                "type": "string"
                            },
                            "cookie_vendor": {
                                "type": "string"
                            },
                            "cookie_name": {
                                "type": "string"
                            },
                        }
                    },
                    "delete": {
                        "type": "string",
                        "format": "date-time",
                    }
                },
                "required": [
                    "cookie",
                    "cookie_vendor",
                    "cookie_name"
                ]
            }
        },
        "birth_date": {
            "type": "object",
            "properties": {
                "year": {
                    "type": "number"
                },
                "month": {
                    "type": "number"
                },
                "date": {
                    "type": "number"
                }
            },
            "required": [
                "year",
            ]
        },
        "gender": {
            "type": "string",
            "enum": ["M", "F"]
        },
        "ban_info": {
            "type": "object",
            "properties": {
                "delete": {
                    "type": "string",
                    "format": "date-time",
                },
                "ban_untill": {
                    "type": "string",
                    "format": "date-time",
                }
            }
        },
        "add_date": {
            "type": "string",
            "format": "date-time",
        },
        "yandex_can_use": {
            "type": "boolean"
        },
        "other_fields": {
            "type": "array",
            "items": {
                "type": "object",
                "properties": {
                    "field_name": {
                        "type": "string"
                    },
                    "field_value": {
                    }
                },
                "required": [
                    "field_name",
                    "field_value"
                ]
            }
        },
        "impulse_fields": {
            "type": "object"
        },
        "required": [
            "external_id",
            "add_date"
        ],
        "title": "Запись про id человека",
        "type": "object"
    }
}


def validate_generator(in_generator, fail_on_errors=True):
    from jsonschema import Draft4Validator
    validator = Draft4Validator(audience_json_schema)
    for rec in in_generator:
        if validator.is_valid(rec):
            # TODO add validate phone, email, id_value
            yield rec
        else:
            for error in sorted(validator.iter_errors(rec), key=str):
                sys.stderr.write(str(error) + "\n")
            if fail_on_errors:
                raise RuntimeError("not valid by json schema " + str(rec) + "\n")


def merge_call(key, recs):
    result = {}
    for rec in recs:
        # first_rec_fields
        for field in ("external_id", "add_date"):
            if field not in result or result[field] is None:
                result[field] = rec[field]
        # join_dict_fields
        for field in ["impulse_fields"]:
            if field in rec and rec[field]:
                result.setdefault(field, {})
                result[field].update(rec[field])
        # replace_fields
        for field in ("birth_date", "gender", "yandex_can_use"):
            if field in rec and rec[field] is not None:
                result[field] = rec[field]
        for field, field_key in (
                ("phones", "id_value"),
                ("emails", "id_value"),
                ("impulses", "impulse"),
                ("other_fields", "field_name"),
                ("cookies", "cookie")
        ):
            if field in rec and rec[field] is not None:
                result.setdefault(field, {})
                try:
                    for value in rec[field]:
                        if field_key in value:
                            # result[field].setdefault(value[field_key], {})
                            # TODO "cookies" smart merge cookie_sync
                            result[field][value[field_key]] = {}
                            for sub_key, sub_value in value.iteritems():
                                result[field][value[field_key]][sub_key] = sub_value
                except Exception:
                    sys.stderr.write(str(rec))
        # TODO "ban_info",
        # TODO process delete
    for field_key in ("phones", "emails", "impulses", "other_fields", "cookies"):
        if field_key in result:
            result[field_key] = result[field_key].values()
    yield result
