# coding=utf-8
from __future__ import unicode_literals, absolute_import

import datetime
from django.db import models
from django.db import transaction
from django.conf import settings
from abc import ABCMeta
from abc import abstractmethod
from api.models import Person
from pay.models import Statement
from pay.models import StatementAccount
from rpc.tool.error_code import gen, CODES
from gm_types.trade import STATEMENT_STATUS
from gm_types.trade import STATEMENT_OPERATION_TYPE
from gm_types.trade import OPERATION_ROLE
from message.views.push import doctor_noti_doctor_ids
TIME_ZONE = settings.TIME_ZONE

__all__ = ['StatementOp', 'CreateOp', 'ApplyOp', 'AuditOp', 'CheckOp', 'RefuseOp', 'SettleOp',
           'BankDefaultOp', 'NameDefaultOp', 'FinResetOp']


class StatementOperation(models.Model):
    class Meta:
        db_table = 'pay_statementoperation'
        app_label = 'pay'

    statement = models.ForeignKey(Statement, verbose_name='关联对账单')
    operator = models.ForeignKey(Person, null=True)
    optype = models.IntegerField(verbose_name='操作类型', choices=STATEMENT_OPERATION_TYPE)
    role = models.IntegerField(choices=OPERATION_ROLE)
    operate_at = models.DateTimeField(null=True, blank=True, verbose_name='操作时间', auto_now_add=True)


class StatementOp(object):
    __metaclass__ = ABCMeta

    optype = 0
    role = 0
    operator = None
    pre_status_list = None

    def __init__(self, statement, operator=None, role=None):
        self.statement = statement
        if operator:
            self.operator = operator

        if role:
            self.role = role
        self.condition_assert()

    def pre_status_assert(self):
        if self.pre_status_list and self.statement.status not in self.pre_status_list:
            raise gen(CODES.STATEMENT_STATUS_ERROR)

    def condition_assert(self):
        self.pre_status_assert()

    @abstractmethod
    def operate(self):
        return

    def update_account(self, modify=False):
        if modify:
            pass
        else:
            doctor_account = self.statement.doctor.merchant.account
            doctor_account_info = {
                'statement': self.statement,
                'province_id': doctor_account.province_id or None,
                'city_id': doctor_account.city_id or None,
                'bank': doctor_account.bank,
                'account_name': doctor_account.account_name,
                'account_number': doctor_account.account_number,
                'account_type': doctor_account.account_type,
                'subbranch': doctor_account.subbranch,
            }
            if getattr(self.statement, 'account', None):
                doctor_account_info.pop('statement')
                account = self.statement.account
                for k, v in doctor_account_info.iteritems():
                    setattr(account, k, v)
                account.save()
            else:
                StatementAccount.objects.create(**doctor_account_info)

    def record(self):
        StatementOperation.objects.create(
            statement=self.statement,
            operator=self.operator,
            optype=self.optype,
            role=self.role,
        )

    def do(self):
        self.condition_assert()
        with transaction.atomic():
            self.operate()
            self.statement.save()
            self.record()


class CreateOp(StatementOp):
    optype = STATEMENT_OPERATION_TYPE.CREATE
    role = OPERATION_ROLE.SYSTEM

    pre_status_list = None

    def operate(self):
        self.statement.status = STATEMENT_STATUS.APPLIED
        self.statement.poundage_rate = getattr(settings, 'SELF_SUPPORT_POUNDAGE_RATE', 0)


# 医生不再需要发起
class ApplyOp(StatementOp):
    # 医生发起结算申请
    optype = STATEMENT_OPERATION_TYPE.APPLY
    role = OPERATION_ROLE.DOCTOR
    # 刚创建, 对运营的审核不满意
    pre_status_list = (STATEMENT_STATUS.CREATED, STATEMENT_STATUS.AUDITED)

    def operate(self):
        self.statement.status = STATEMENT_STATUS.APPLIED


class AuditOp(StatementOp):
    # 运营审核完成修改金额
    optype = STATEMENT_OPERATION_TYPE.AUDIT
    role = OPERATION_ROLE.STAFF
    pre_status_list = (STATEMENT_STATUS.VOID, STATEMENT_STATUS.APPLIED, STATEMENT_STATUS.REFUSED, STATEMENT_STATUS.CREATED)
    message = u'{}医生您好！您{}年{}月的对账单已经审核完成，请尽快确认本月结算金额，' \
              u'在电脑上登录医生后台即可查看。如有问题，请联系您的商务。'

    def operate(self):
        self.statement.status = STATEMENT_STATUS.AUDITED
        self.statement.settle_amount = self.statement.settle_price
        message = self.message.format(self.statement.doctor.name, self.statement.statement_date / 100,
                                      str(self.statement.statement_date)[4:])
        doctor_noti_doctor_ids(doctor_ids=[self.statement.doctor.id], alert=message)


class CheckOp(StatementOp):
    optype = STATEMENT_OPERATION_TYPE.CHECK
    role = OPERATION_ROLE.DOCTOR
    pre_status_list = (STATEMENT_STATUS.AUDITED,)

    def operate(self):
        self.statement.status = STATEMENT_STATUS.CHECKED
        self.statement.merchant_confirm_time = datetime.datetime.now()
        self.update_account(modify=True)
        self.statement.check_snapshot = self.statement.get_snapshot()


class RefuseOp(StatementOp):
    optype = STATEMENT_OPERATION_TYPE.REFUSE
    role = OPERATION_ROLE.DOCTOR
    pre_status_list = (STATEMENT_STATUS.AUDITED,)

    def operate(self):
        self.statement.status = STATEMENT_STATUS.REFUSED


class SettleOp(StatementOp):
    optype = STATEMENT_OPERATION_TYPE.SETTLE
    role = OPERATION_ROLE.STAFF
    pre_status_list = (STATEMENT_STATUS.CHECKED, STATEMENT_STATUS.DEFEAT)
    message = '{}医生您好！您{}年{}月的结算金额已经打款到账，请到您的对应账户查看。如有问题，请联系您的商务。'

    def operate(self):
        self.update_account(modify=True)
        self.statement.status = STATEMENT_STATUS.SETTLED
        self.statement.settle_time = datetime.datetime.now()
        self.statement.settle_snapshot = self.statement.get_snapshot()
        for order in self.statement.orders.all():
            order.is_settled = True
            order.save()
        message = self.message.format(self.statement.doctor.name, self.statement.statement_date / 100,
                                      str(self.statement.statement_date)[4:])
        doctor_noti_doctor_ids(doctor_ids=[self.statement.doctor.id], alert=message)


class BankDefaultOp(StatementOp):
    optype = STATEMENT_OPERATION_TYPE.BANK_DEFEAT
    role = OPERATION_ROLE.STAFF
    pre_status_list = (STATEMENT_STATUS.CHECKED, STATEMENT_STATUS.DEFEAT,)

    def operate(self):
        self.update_account(modify=True)
        self.statement.status = STATEMENT_STATUS.DEFEAT


class NameDefaultOp(StatementOp):
    optype = STATEMENT_OPERATION_TYPE.NAME_DEFEAT
    role = OPERATION_ROLE.STAFF
    pre_status_list = (STATEMENT_STATUS.CHECKED, STATEMENT_STATUS.DEFEAT, )

    def operate(self):
        self.update_account(modify=True)
        self.statement.status = STATEMENT_STATUS.DEFEAT


class FinResetOp(StatementOp):
    optype = STATEMENT_OPERATION_TYPE.FIN_RESET
    role = OPERATION_ROLE.STAFF
    pre_status_list = (STATEMENT_STATUS.SETTLED, STATEMENT_STATUS.DEFEAT,)

    def operate(self):
        self.statement.status = STATEMENT_STATUS.CHECKED
