# This file is part of Tryton.  The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from __future__ import with_statement

from datetime import datetime, time
from trytond.model import Workflow, ModelView, ModelSQL, fields
from trytond.pyson import Eval, If, In, Get
from trytond.transaction import Transaction
from trytond.pool import Pool
from trytond.wizard import (Wizard, StateView, Button, StateReport, StateTransition)
from trytond.report import Report
from .exceptions import CustomerServiceError, CrmConfigurationError
from trytond.i18n import gettext

STATES = {
    'readonly': (Eval('state') != 'draft'),
}

STATES_OPEN = {
    'readonly': (Eval('state') != 'open'),
    'required': (Eval('state') == 'close'),
}

STATES_CLOSE = {
    'readonly': (Eval('state') != 'open'),
    'required': (Eval('state') == 'close'),
}

OPTION_SELECT = [
    ('', ''),
    ('N.A', 'N.A'),
    ('yes', 'Yes'),
    ('no', 'No'),
]

SEXUAL_DIVERSITY = [
    ('N.A', 'N.A'),
    ('', 'HETEROSEXUAL'),
    ('', 'GAY'),
    ('', 'LESBIANA'),
    ('', 'TRAVESTI'),
    ('', 'BISEXUAL')
]

ETHNICAL_GROUP = [
    ('N.A', 'N.A'),
    ('afrocolombiano', 'AFROCOLOMBIANO'),
    ('palenquero', 'PALENQUERO'),
    ('indigena', 'INDÍGENA'),
    ('raizal', 'RAIZAL'),
    ('rom_gitano', 'ROM O GITANO'),
    ('mulato', 'MULATO'),
]


class CustomerService(Workflow, ModelSQL, ModelView):
    'Customer Service'
    __name__ = 'crm.customer_service'
    _rec_name = 'number'
    number = fields.Char('Number', readonly=True)
    reference = fields.Char('Reference', states=STATES)
    party = fields.Many2One('party.party', 'Party',
        states={
            'readonly': (Eval('state') != 'draft'),
            # 'required': (Eval('state') == 'open'),
        }, select=True)
    route = fields.Char('Route', states=STATES, select=True)
    customer = fields.Char('Customer', states=STATES,
            required=True, select=True)
    address = fields.Char('Address', states=STATES, select=True,
            depends=['party'])
    phone = fields.Char('Phone', states=STATES, select=True,
            depends=['party'])
    city = fields.Char('City', states=STATES, select=True,
            depends=['party'])
    case = fields.Many2One('crm.case', 'Case', states=STATES,
        required=True, domain=[
            ('parent', '!=', None),
        ])
    description = fields.Text('Description', required=True, states=STATES)
    diagnose = fields.Text('Diagnose', states=STATES_CLOSE)
    response = fields.Text('Response', states=STATES_CLOSE)
    cs_date = fields.DateTime('Date', states={'readonly': True})
    effective_date = fields.DateTime('Effective Datetime',
        states=STATES_CLOSE, depends=['state'])
    responsible_employee = fields.Many2One('company.employee',
        'Responsible', states=STATES_CLOSE)
    company = fields.Many2One('company.company', 'Company', required=True,
        states=STATES, domain=[
            ('id', If(In('company',
                Eval('context', {})), '=', '!='), Get(Eval('context', {}),
                'company', 0)), ])
    satisfaction = fields.Selection(OPTION_SELECT, 'Satisfaction')
    satisfaction_string = satisfaction.translated('satisfaction')
    notes = fields.Text('Notes', states=STATES_CLOSE)
    efficacy = fields.Function(fields.Selection([
            ('on_time', 'On Time'),
            ('delay', 'Delay'),
            ],  'Efficacy', states=STATES_CLOSE), 'get_efficacy')
    efficacy_string = efficacy.translated('efficacy')
    department = fields.Many2One('company.department', 'Department')
    category_customer = fields.Many2One('party.category', 'Category',
            required=False, states=STATES)
    state = fields.Selection([
            ('draft', 'Draft'),
            ('open', 'Open'),
            ('close', 'Close'),
            ('cancelled', 'Cancelled'),
        ], 'State', readonly=True, required=True)
    state_string = state.translated('state')
    media = fields.Selection([
            ('', ''),
            ('phone', 'Phone'),
            ('fax', 'Fax'),
            ('mail', 'Mail'),
            ('chat', 'Chat'),
            ('direct', 'Direct'),
            ('web', 'Web'),
            ('survey', 'Survey'),
            ('other', 'Other'),
        ], 'Media', states=STATES)
    action_plan = fields.Text('Action Plan', states=STATES_CLOSE)
    age = fields.Function(fields.Char('Age'), 'get_age')

    @classmethod
    def __setup__(cls):
        super(CustomerService, cls).__setup__()
        cls._order.insert(0, ('create_date', 'DESC'))
        cls._order.insert(1, ('id', 'DESC'))
        cls._transitions |= set((
                ('draft', 'open'),
                ('open', 'draft'),
                ('open', 'close'),
                ('open', 'cancelled'),
                ('close', 'cancelled'),
                ('cancelled', 'draft'),
                ))
        cls._buttons.update({
                'draft': {
                    'invisible': Eval('state').in_(['draft','close'])
                    },
                'cancel': {
                    'invisible': Eval('state') == 'cancelled',
                    },
                'open': {
                    'invisible': Eval('state') != 'draft',
                    },
                'close': {
                    'invisible': Eval('state') != 'open',
                    },
                })

    @staticmethod
    def default_company():
        return Transaction().context.get('company') or False

    @staticmethod
    def default_state():
        return 'draft'

    @staticmethod
    def default_cs_date():
        return datetime.now()

    @staticmethod
    def default_efficacy():
        return 'on_time'

    def get_age(self, name=None):
        if self.party:
            self.age = self.party.age

    @classmethod
    @ModelView.button
    @Workflow.transition('open')
    def open(cls, services):
        for service in services:
            if not service.number:
                service.set_number()
        return {}

    @classmethod
    @ModelView.button
    @Workflow.transition('cancelled')
    def cancel(cls, services):
        pass

    @classmethod
    @ModelView.button
    @Workflow.transition('draft')
    def draft(cls, services):
        pass

    @classmethod
    @ModelView.button
    @Workflow.transition('close')
    def close(cls, services):
        for service in services:
            pass

    @classmethod
    def delete(cls, records):
        for record in records:
            if record.number:
                raise CustomerServiceError(
                    gettext('crm.msg_delete_numbered', s=record.number)
                )
        super(CustomerService, cls).delete(records)

    @fields.depends('party', 'address', 'phone')
    def on_change_party(self):
        if self.party:
            if hasattr(self.party, 'street') and hasattr(self.party, 'city_name'):
                self.address = self.party.street
                self.city = self.party.city_name
            self.phone = self.party.phone or self.party.mobile
            self.customer = self.party.name

    def get_efficacy(self, name):
        pool = Pool()
        config = pool.get('crm.configuration')(1)
        now = datetime.now()
        if not config.efficay_hour_limit:
            raise CrmConfigurationError(
                gettext('crm.msg_missing_configuration_limit_hour'))
        val_hr = config.efficay_hour_limit
        if self.effective_date:
            lapse = self.effective_date - self.create_date
        else:
            lapse = now - self.create_date
        lapse_hours = (lapse.days * 24) + (lapse.seconds / 3600)
        if lapse_hours >= val_hr:
            return 'delay'
        else:
            return 'on_time'

    def set_number(self):
        '''
        Set sequence number
        '''
        pool = Pool()
        config = pool.get('crm.configuration')(1)
        if not config.customer_service_sequence:
            raise CrmConfigurationError(
                gettext('crm.msg_missing_sequence_customer_service'))
        seq = config.customer_service_sequence.get()
        self.write([self], {'number': seq})


class PrintCustomerService(Wizard):
    'Print Customer Service Report'
    __name__ = 'crm.customer_service.print'
    start = StateTransition()
    print_ = StateReport('crm.customer_service')

    def transition_start(self):
        self.services_ids = Transaction().context['active_ids'][:1]
        return 'print_'

    def do_print_(self, action):
        data = {}
        data['id'] = self.services_ids.pop()
        data['ids'] = [data['id']]
        Service = Pool().get('crm.customer_service')
        service = Service(data['id'])
        try:
            action['email'] = eval(action['email'])
        except:
            action['email'] = {}
        if service and service.party.email:
            action['email'].update({"to": service.party.email})
        return action, data

    def transition_print_(self):
        if self.services_ids:
            return 'print_'
        return 'end'


class CustomerServiceReport(Report):
    __name__ = 'crm.customer_service'

    @classmethod
    def get_context(cls, records, header, data):
        report_context = super().get_context(records, header, data)
        Company = Pool().get('company.company')
        report_context['company'] = Company(Transaction().context.get('company'))
        return report_context


class CustomerServiceIndicatorsStart(ModelView):
    'Customer Service Indicators Start'
    __name__ = 'crm.customer_service_indicators.start'
    company = fields.Many2One('company.company', 'Company', required=True)
    fiscalyear = fields.Many2One('account.fiscalyear', 'Fiscal Year',
        required=True)
    department = fields.Many2One('company.department', 'Department')
    case = fields.Many2One('crm.case', 'Case', domain=[
                ('parent', '!=', None),
            ])

    @staticmethod
    def default_company():
        return Transaction().context.get('company')

    @staticmethod
    def default_fiscalyear():
        FiscalYear = Pool().get('account.fiscalyear')
        return FiscalYear.find(
            Transaction().context.get('company'), exception=False)


class CustomerServiceIndicators(Wizard):
    'Customer Service Indicators'
    __name__ = 'crm.customer_service_indicators'
    start = StateView('crm.customer_service_indicators.start',
        'crm.customer_service_indicators_start_view_form', [
        Button('Cancel', 'end', 'tryton-cancel'),
        Button('Print', 'print_', 'tryton-ok', default=True),
        ])
    print_ = StateReport('crm.customer_service_indicators_report')

    def do_print_(self, action):
        department_id = None
        if self.start.department:
            department_id = self.start.department.id

        data = {
            'company': self.start.company.id,
            'department': department_id,
            'fiscalyear': self.start.fiscalyear.id,
            }
        return action, data

    def transition_print_(self):
        return 'end'


class CustomerServiceIndicatorsReport(Report):
    'Customer Service Indicators Report'
    __name__ = 'crm.customer_service_indicators_report'

    @classmethod
    def get_context(cls, records, header, data):
        report_context = super().get_context(records, header, data)
        pool = Pool()
        Company = pool.get('company.company')
        Fiscalyear = pool.get('account.fiscalyear')
        Service = pool.get('crm.customer_service')
        Department = pool.get('company.department')
        fiscalyear = Fiscalyear(data['fiscalyear'])
        company = Company(data['company'])
        department = None
        records = {}

        def _get_months():
            return {'month' + str(i + 1): [] for i in range(12)}

        start = datetime.combine(fiscalyear.start_date, time(0, 0))
        end = datetime.combine(fiscalyear.end_date, time(0, 0))
        dom_service = [
                ('cs_date', '>=', start),
                ('cs_date', '<=', end),
                ('state', '!=', 'cancelled'),
        ]

        if data['department']:
            dom_service.append(
                ('department', '=', data['department']),
            )
            department = Department(data['department'])

        services = Service.search(dom_service)

        months = {(i): [] for i in range(1, 13)}
        annual_req = []

        for service in services:
            if service.case.id not in records.keys():
                records[service.case.id] = {
                    'name': service.case.name,
                    'total': [],
                    'rate_total': [],
                }
                records[service.case.id].update(_get_months())
            month_number = service.cs_date.date().month
            months[month_number].append(1)
            annual_req.append(1)
            records[service.case.id]['month' + str(month_number)].append(1)
            records[service.case.id]['total'].append(1)

        annual_req = sum(annual_req)

        for value in records.values():
            total_req = 0
            for i in range(1, 13):
                if sum(months[i]) > 0:
                    value['rate' + str(i)] = sum(value['month' + str(i)]) / sum(months[i]) * 100
                    total_req += sum(value['month' + str(i)])
                else:
                    value['rate' + str(i)] = None
            if annual_req > 0:
                value['rate_total'] = (total_req / annual_req) * 100

        report_context['department'] = department
        report_context['records'] = records.values()
        report_context['fiscalyear'] = Fiscalyear(data['fiscalyear'])
        report_context['company'] = company.party.name
        return report_context


class EfficacyMonthStart(ModelView):
    'Efficacy Month Start'
    __name__ = 'crm.efficacy_month.start'
    company = fields.Many2One('company.company', 'Company', required=True)
    fiscalyear = fields.Many2One('account.fiscalyear', 'Fiscal Year',
        required=True)
    month = fields.Many2One('account.period', 'Month',
            depends=['fiscalyear'], required=True, domain=[
                ('fiscalyear', '=', Eval('fiscalyear')),
            ])

    @staticmethod
    def default_company():
        return Transaction().context.get('company')

    @staticmethod
    def default_fiscalyear():
        FiscalYear = Pool().get('account.fiscalyear')
        return FiscalYear.find(
            Transaction().context.get('company'), exception=False)


class EfficacyMonth(Wizard):
    'Efficacy Month'
    __name__ = 'crm.efficacy_month'
    start = StateView('crm.efficacy_month.start',
        'crm.efficacy_month_start_view_form', [
            Button('Cancel', 'end', 'tryton-cancel'),
            Button('Print', 'print_', 'tryton-ok', default=True),
            ])
    print_ = StateReport('crm.efficacy_month_report')

    def do_print_(self, action):

        data = {
            'company': self.start.company.id,
            'fiscalyear': self.start.fiscalyear.id,
            'period': self.start.month.id,
        }
        return action, data

    def transition_print_(self):
        return 'end'


class EfficacyMonthReport(Report):
    'Efficacy Month Report'
    __name__ = 'crm.efficacy_month_report'

    @classmethod
    def _get_records_months(cls, services):
        records_months = {}

        def _get_months():
            return {'month' + str(i + 1): {'num_req': 0, 'efficacy': 0, 'rate': None}
                    for i in range(12)}

        total_months = {}

        for i in range(12):
            total_months['total_month' + str(i + 1)] = []

        for service in services:
            if service.department.id not in records_months.keys():
                records_months[service.department.id] = {
                    'name': service.department.name,
                    'total': [],
                }
                records_months[service.department.id].update(_get_months())

            month = 'month' + str(service.cs_date.date().month)
            records_months[service.department.id][month]['num_req'] += 1

            if service.efficacy == 'on_time':
                records_months[service.department.id][month]['efficacy'] += 1

        for department in records_months.values():
            total_efficacy = []
            total_num_req = []
            for i in range(12):
                department_month = department['month' + str(i + 1)]
                if department_month['num_req'] > 0:
                    rate_efficacy = (department_month['efficacy'] / department_month['num_req']) * 100
                    department['month' + str(i + 1)] = rate_efficacy
                    total_months['total_month' + str(i + 1)].append({'efficacy': department_month['efficacy'], 'num_req': department_month['num_req']})
                    total_efficacy.append(department_month['efficacy'])
                    total_num_req.append(department_month['num_req'])
                else:
                    department['month' + str(i + 1)] = None
            if sum(total_num_req) > 0:
                department['total'] = sum(total_efficacy) / sum(total_num_req) * 100

        annual_efficacy = []
        effective_months = 0
        for k, v in total_months.items():
            if v == []:
                total_months[k] = None
            else:
                effective_months += 1
                efficacy = [j['efficacy'] for j in v]
                num_req = [j['num_req'] for j in v]
                total_months[k] = (sum(efficacy) / sum(num_req)) * 100
                annual_efficacy.append(total_months[k])

        res_annual_efficacy = None
        if effective_months > 0:
            res_annual_efficacy = sum(annual_efficacy) / effective_months
        total_months['annual_efficacy'] = res_annual_efficacy
        return records_months, total_months

    @classmethod
    def _get_departments(cls, services):
        departments = {}

        for service in services:
            if service.department.id not in departments.keys():
                departments[service.department.id] = {
                    'name': service.department.name,
                    'num_req': [],
                    'efficacy': [],
                    'satisfaction': [],
                }

            departments[service.department.id]['num_req'].append(1)
            if service.efficacy == 'on_time':
                departments[service.department.id]['efficacy'].append(1)

            if service.satisfaction == 'yes':
                departments[service.department.id]['satisfaction'].append(1)

        total_num_req = 0
        total_efficacy = 0
        total_rate = 0
        total_satisfaction = 0
        rate_total_satisfaction = 0

        for k, v in departments.items():
            v['num_req'] = sum(v['num_req'])
            v['efficacy'] = sum(v['efficacy'])
            v['satisfaction'] = sum(v['satisfaction'])
            rate_satisfaction = 0
            if v['satisfaction'] > 0:
                rate_satisfaction = (v['satisfaction'] / v['num_req']) * 100
            v['rate_satisfaction'] = rate_satisfaction
            rate = 0
            if v['num_req'] > 0:
                rate = (v['efficacy'] / v['num_req']) * 100
            v['rate'] = rate
            total_num_req += v['num_req']
            total_efficacy += v['efficacy']
            total_satisfaction += v['satisfaction']
        if total_satisfaction > 0:
            rate_total_satisfaction += (total_satisfaction / total_num_req) * 100
        if total_num_req > 0:
            total_rate += (total_efficacy / total_num_req) * 100

        values = {
            'total_num_req': total_num_req,
            'total_efficacy': total_efficacy,
            'total_rate': total_rate,
            'rate_total_satisfaction': rate_total_satisfaction,
            'total_satisfaction': total_satisfaction,
        }
        return departments, values

    @classmethod
    def get_context(cls, records, header, data):
        report_context = super().get_context(records, header, data)
        pool = Pool()
        Fiscalyear = pool.get('account.fiscalyear')
        Company = pool.get('company.company')
        Period = pool.get('account.period')
        Service = pool.get('crm.customer_service')
        company = Company(data['company'])
        period = Period(data['period'])
        records = {}
        start = datetime.combine(period.start_date, time(0,0))
        end = datetime.combine(period.end_date, time(23,59))

        dom_service = [
                ('cs_date', '>=', start),
                ('cs_date', '<=', end),
                ('state', '!=', 'cancelled'),
        ]

        services = Service.search(dom_service)
        departments, values = cls._get_departments(services)
        records_months, total_months = cls._get_records_months(services)

        report_context.update(values)
        report_context.update(total_months)
        report_context['records'] = departments.values()
        report_context['records_months'] = records_months.values()
        report_context['fiscalyear'] = Fiscalyear(data['fiscalyear'])
        report_context['month'] = period.name
        report_context['company'] = company.party.name
        return report_context


class MonitoringReport(Report):
    'Monitoring Report'
    __name__ = 'crm.monitoring_report'

    @classmethod
    def get_context(cls, records, header, data):
        report_context = super().get_context(records, header, data)
        user = Pool().get('res.user')(Transaction().user)
        report_context['company'] = user.company
        return report_context
