# -*- coding: utf-8 -*-

# Copyright(C) 2012 Romain Bignon
#
# This file is part of weboob.
#
# weboob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# weboob is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.


from weboob.deprecated.mech import ClientForm
ControlNotFoundError = ClientForm.ControlNotFoundError

from decimal import Decimal
import re

from weboob.deprecated.mech import ClientForm
from weboob.tools.ordereddict import OrderedDict
from weboob.deprecated.browser import Page, BrokenPageError, BrowserUnavailable, BrowserIncorrectPassword
from weboob.capabilities import NotAvailable
from weboob.capabilities.bank import Account, Investment
from weboob.tools.capabilities.bank.transactions import FrenchTransaction


class _LogoutPage(Page):
    def on_loaded(self):
        try:
            raise BrowserIncorrectPassword(self.parser.tocleanstring(self.parser.select(self.document.getroot(), '.messErreur', 1)))
        except BrokenPageError:
            pass


class LoginPage(_LogoutPage):
    def login(self, login):
        self.browser.select_form(name='Main')
        self.browser.set_all_readonly(False)
        self.browser['ctl01$CC_ind_pauthpopup$ctl01$CC_ind_ident$ctl01$CC_ind_inputuserid_sup$txnuabbd'] = login.encode('utf-8')
        self.browser['__EVENTTARGET'] = 'ctl01$CC_ind_pauthpopup$ctl01$CC_ind_ident$ctl01$CC_ind_inputuserid_sup$btnValider'
        self.browser.submit(nologin=True)

    def login2(self, nuser, passwd):
        self.browser.select_form(name='Main')
        self.browser.set_all_readonly(False)
        self.browser['__EVENTARGUMENT'] = 'idsrv=WE'

        m = None
        try:
            a = self.document.xpath('//a[@title="Valider"]')[0]
        except IndexError:
            pass
        else:
            m = re.match("javascript:RedirectToDeiPro\('([^']+)', \d+\);", a.attrib['href'])

        if m:
            self.browser['nuusager'] = nuser.encode('utf-8')
            self.browser['codconf'] = passwd.encode('utf-8')
            self.browser.form.action = m.group(1)

        self.browser.submit(nologin=True)

        return m is not None

    def login3(self, passwd):
        self.browser.select_form(name='Main')
        self.browser['codconf'] = passwd.encode('utf-8')
        a = self.document.xpath('//a[@title="Valider"]')[0]
        m = re.match("javascript:RedirectToDeiPart\('([^']+)'\);", a.attrib['href'])
        if not m:
            raise BrokenPageError('Unable to find validate URL')
        self.browser.form.action = m.group(1)
        self.browser.submit(nologin=True)


class ErrorPage(_LogoutPage):
    pass


class UnavailablePage(Page):
    def on_loaded(self):
        try:
            raise BrowserUnavailable(self.parser.select(self.document.getroot(), 'div#message_error_hs', 1).text.strip())
        except BrokenPageError:
            raise BrowserUnavailable()


class Transaction(FrenchTransaction):
    PATTERNS = [(re.compile('^CB (?P<text>.*?) FACT (?P<dd>\d{2})(?P<mm>\d{2})(?P<yy>\d{2})', re.IGNORECASE),
                                                            FrenchTransaction.TYPE_CARD),
                (re.compile('^RET(RAIT)? DAB (?P<dd>\d+)-(?P<mm>\d+)-.*', re.IGNORECASE),
                                                            FrenchTransaction.TYPE_WITHDRAWAL),
                (re.compile('^RET(RAIT)? DAB (?P<text>.*?) (?P<dd>\d{2})(?P<mm>\d{2})(?P<yy>\d{2}) (?P<HH>\d{2})H(?P<MM>\d{2})', re.IGNORECASE),
                                                            FrenchTransaction.TYPE_WITHDRAWAL),
                (re.compile('^VIR(EMENT)?(\.PERIODIQUE)? (?P<text>.*)', re.IGNORECASE),
                                                            FrenchTransaction.TYPE_TRANSFER),
                (re.compile('^PRLV (?P<text>.*)', re.IGNORECASE),
                                                            FrenchTransaction.TYPE_ORDER),
                (re.compile('^CHEQUE.*', re.IGNORECASE),    FrenchTransaction.TYPE_CHECK),
                (re.compile('^(CONVENTION \d+ )?COTIS(ATION)? (?P<text>.*)', re.IGNORECASE),
                                                            FrenchTransaction.TYPE_BANK),
                (re.compile(r'^\* (?P<text>.*)', re.IGNORECASE),
                                                            FrenchTransaction.TYPE_BANK),
                (re.compile('^REMISE (?P<text>.*)', re.IGNORECASE),
                                                            FrenchTransaction.TYPE_DEPOSIT),
                (re.compile('^(?P<text>.*)( \d+)? QUITTANCE .*', re.IGNORECASE),
                                                            FrenchTransaction.TYPE_ORDER),
                (re.compile('^CB [\d\*]+ (?P<text>.*)', re.IGNORECASE),
                                                            FrenchTransaction.TYPE_CARD),
               ]


class IndexPage(Page):
    ACCOUNT_TYPES = {u'Epargne liquide':            Account.TYPE_SAVINGS,
                     u'Compte Courant':             Account.TYPE_CHECKING,
                     u'Mes comptes':                Account.TYPE_CHECKING,
                     u'Mon épargne':                Account.TYPE_SAVINGS,
                    }

    def _get_account_info(self, a):
        m = re.search("PostBack(Options)?\([\"'][^\"']+[\"'],\s*['\"]([HISTORIQUE_\w|SYNTHESE_ASSURANCE_CNP|BOURSE][\d\w&]+)?['\"]", a.attrib.get('href', ''))
        if m is None:
            return None
        else:
            # it is in form CB&12345[&2]. the last part is only for new website
            # and is necessary for navigation.
            link = m.group(2)
            parts = link.split('&')
            info = {}
            if len(parts) > 1:
                info['type'] = parts[0]
                info['id'] = parts[1]
            else:
                id = re.search("([\d]+)", a.attrib.get('title'))
                info['type'] = link
                info['id'] = id.group(1)
            if info['type'] == 'SYNTHESE_ASSURANCE_CNP':
                info['acc_type'] = Account.TYPE_LIFE_INSURANCE
            if info['type'] == 'BOURSE':
                info['acc_type'] = Account.TYPE_MARKET
            info['link'] = link
            return info


    def _add_account(self, accounts, link, label, account_type, balance):
        info = self._get_account_info(link)
        if info is None:
            self.logger.warning('Unable to parse account %r: %r' % (label, link))
            return

        account = Account()
        account.id = info['id']
        account._info = info
        account.label = label
        account.type = info['acc_type'] if 'acc_type' in info else account_type
        account.balance = Decimal(FrenchTransaction.clean_amount(balance)) if balance else self.get_balance(account)
        account.currency = account.get_currency(balance)
        account._card_links = []

        if account._info['type'] == 'HISTORIQUE_CB' and account.id in accounts:
            a = accounts[account.id]
            if not a.coming:
                a.coming = Decimal('0.0')
            a.coming += account.balance
            a._card_links.append(account._info)
            return

        accounts[account.id] = account

    def get_balance(self, account):
        if not account.type == Account.TYPE_LIFE_INSURANCE:
            return NotAvailable
        self.go_history(account._info)
        balance = self.browser.page.document.xpath('.//tr[td[contains(text(), ' + account.id + ')]]/td[contains(@class, "somme")]')
        if len(balance) > 0:
            balance = self.parser.tocleanstring(balance[0])
            balance = Decimal(FrenchTransaction.clean_amount(balance)) if balance != u'' else NotAvailable
        else:
            balance = NotAvailable
        self.go_list()
        return balance

    def get_list(self):
        accounts = OrderedDict()

        # Old website
        for table in self.document.xpath('//table[@cellpadding="1"]'):
            account_type = Account.TYPE_UNKNOWN
            for tr in table.xpath('./tr'):
                tds = tr.findall('td')
                if tr.attrib.get('class', '') == 'DataGridHeader':
                    account_type = self.ACCOUNT_TYPES.get(tds[1].text.strip(), Account.TYPE_UNKNOWN)
                else:
                    # On the same row, there are many accounts (for example a
                    # check accound and a card one).
                    for i, a in enumerate(tds[2].xpath('./a')):
                        label = self.parser.tocleanstring(a)
                        balance = self.parser.tocleanstring(tds[-2].xpath('./a')[i])
                        self._add_account(accounts, a, label, account_type, balance)

        if len(accounts) == 0:
            # New website
            for table in self.document.xpath('//div[@class="panel"]'):
                title = table.getprevious()
                if title is None:
                    continue
                account_type = self.ACCOUNT_TYPES.get(self.parser.tocleanstring(title), Account.TYPE_UNKNOWN)
                for tr in table.xpath('.//tr'):
                    tds = tr.findall('td')
                    for i in xrange(len(tds)):
                        a = tds[i].find('a')
                        if a is not None:
                            break

                    if a is None:
                        continue

                    label = self.parser.tocleanstring(tds[0])
                    balance = self.parser.tocleanstring(tds[-1])

                    self._add_account(accounts, a, label, account_type, balance)

        return accounts.itervalues()

    def go_list(self):
        self.browser.select_form(name='main')
        self.browser.set_all_readonly(False)
        self.browser['__EVENTARGUMENT'] = 'CPTSYNT0'
        self.browser.controls.append(ClientForm.TextControl('text', 'm_ScriptManager', {'value': ''}))

        # Ugly check to determine if we are on the new or old website.
        try:
            self.browser['MM$m_CH$IsMsgInit']
        except ControlNotFoundError:
            self.logger.debug('New website')
            self.browser['__EVENTTARGET'] = 'MM$m_PostBack'
            self.browser['m_ScriptManager'] = 'MM$m_UpdatePanel|MM$m_PostBack'
        else:
            self.logger.debug('Old website')
            self.browser['__EVENTTARGET'] = 'Menu_AJAX'
            self.browser['m_ScriptManager'] = 'm_ScriptManager|Menu_AJAX'

        try:
            self.browser.controls.remove(self.browser.find_control(name='Cartridge$imgbtnMessagerie', type='image'))
            self.browser.controls.remove(self.browser.find_control(name='MM$m_CH$ButtonImageFondMessagerie', type='image'))
            self.browser.controls.remove(self.browser.find_control(name='MM$m_CH$ButtonImageMessagerie', type='image'))
        except ControlNotFoundError:
            pass
        self.browser.submit()

    def go_history(self, info):
        self.browser.select_form(name='main')
        self.browser.set_all_readonly(False)
        self.browser['__EVENTTARGET'] = 'MM$SYNTHESE'
        self.browser['__EVENTARGUMENT'] = info['link']
        try:
            self.browser['MM$m_CH$IsMsgInit'] = '0'
        except ControlNotFoundError:
            # Not available on new website.
            pass
        self.browser.controls.append(ClientForm.TextControl('text', 'm_ScriptManager', {'value': ''}))
        self.browser['m_ScriptManager'] = 'MM$m_UpdatePanel|MM$SYNTHESE'
        try:
            self.browser.controls.remove(self.browser.find_control(name='Cartridge$imgbtnMessagerie', type='image'))
            self.browser.controls.remove(self.browser.find_control(name='MM$m_CH$ButtonImageFondMessagerie', type='image'))
            self.browser.controls.remove(self.browser.find_control(name='MM$m_CH$ButtonImageMessagerie', type='image'))
        except ControlNotFoundError:
            pass
        self.browser.submit()

    def get_history(self):
        i = 0
        ignore = False
        for tr in self.document.xpath('//table[@cellpadding="1"]/tr') + self.document.xpath('//tr[@class="rowClick" or @class="rowHover"]'):
            tds = tr.findall('td')

            if len(tds) < 4:
                continue

            # if there are more than 4 columns, ignore the first one.
            i = min(len(tds) - 4, 1)

            if tr.attrib.get('class', '') == 'DataGridHeader':
                if tds[2].text == u'Titulaire':
                    ignore = True
                else:
                    ignore = False
                continue

            if ignore:
                continue

            # Remove useless details
            detail = tr.cssselect('div.detail')
            if len(detail) > 0:
                detail[0].drop_tree()

            t = Transaction(i)

            date = u''.join([txt.strip() for txt in tds[i+0].itertext()])
            raw = u' '.join([txt.strip() for txt in tds[i+1].itertext()])
            debit = u''.join([txt.strip() for txt in tds[-2].itertext()])
            credit = u''.join([txt.strip() for txt in tds[-1].itertext()])

            t.parse(date, re.sub(r'[ ]+', ' ', raw))
            if t.date is NotAvailable:
                continue
            t.set_amount(credit, debit)
            yield t

            i += 1

    def go_next(self):
        # <a id="MM_HISTORIQUE_CB_lnkSuivante" class="next" href="javascript:WebForm_DoPostBackWithOptions(new WebForm_PostBackOptions(&quot;MM$HISTORIQUE_CB$lnkSuivante&quot;, &quot;&quot;, true, &quot;&quot;, &quot;&quot;, false, true))">Suivant<span class="arrow">></span></a>

        link = self.document.xpath('//a[contains(@id, "lnkSuivante")]')
        if len(link) == 0 or 'disabled' in link[0].attrib:
            return False

        account_type = 'COMPTE'
        m = re.search('HISTORIQUE_(\w+)', link[0].attrib['href'])
        if m:
            account_type = m.group(1)

        self.browser.select_form(name='main')
        self.browser.set_all_readonly(False)
        self.browser['__EVENTTARGET'] = 'MM$HISTORIQUE_%s$lnkSuivante' % account_type
        self.browser['__EVENTARGUMENT'] = ''
        try:
            self.browser['MM$m_CH$IsMsgInit'] = 'N'
        except ControlNotFoundError:
            # New website
            pass
        self.browser.controls.append(ClientForm.TextControl('text', 'm_ScriptManager', {'value': ''}))
        self.browser['m_ScriptManager'] = 'MM$m_UpdatePanel|MM$HISTORIQUE_COMPTE$lnkSuivante'
        try:
            self.browser.controls.remove(self.browser.find_control(name='Cartridge$imgbtnMessagerie', type='image'))
            self.browser.controls.remove(self.browser.find_control(name='MM$m_CH$ButtonImageFondMessagerie', type='image'))
            self.browser.controls.remove(self.browser.find_control(name='MM$m_CH$ButtonImageMessagerie', type='image'))
        except ControlNotFoundError:
            pass
        self.browser.submit()

        return True

    def go_life_insurance(self, account):
        link = self.document.xpath('//table[@summary="Mes contrats d\'assurance vie"]/tbody/tr[td[contains(text(), ' + account.id + ') ]]//a')[0]
        m = re.search("PostBack(Options)?\([\"'][^\"']+[\"'],\s*['\"](REDIR_ASS_VIE[\d\w&]+)?['\"]", link.attrib.get('href', ''))
        if m is not None:
            self.browser.select_form(name='main')
            self.browser.set_all_readonly(False)
            self.browser['__EVENTTARGET'] = 'MM$SYNTHESE_ASSURANCE_CNP'
            self.browser['__EVENTARGUMENT'] = m.group(2)
            try:
                self.browser['MM$m_CH$IsMsgInit'] = '0'
            except ControlNotFoundError:
                # Not available on new website.
                pass
            self.browser.controls.append(ClientForm.TextControl('text', 'm_ScriptManager', {'value': ''}))
            self.browser['m_ScriptManager'] = 'MM$m_UpdatePanel|MM$SYNTHESE'
            try:
                self.browser.controls.remove(self.browser.find_control(name='Cartridge$imgbtnMessagerie', type='image'))
                self.browser.controls.remove(self.browser.find_control(name='MM$m_CH$ButtonImageFondMessagerie', type='image'))
                self.browser.controls.remove(self.browser.find_control(name='MM$m_CH$ButtonImageMessagerie', type='image'))
            except ControlNotFoundError:
                pass
            self.browser.submit()


class MarketPage(Page):
    def is_error(self):
        try:
            return self.document.xpath('//caption')[0].text == "Erreur"
        except IndexError:
            return False


    def parse_decimal(self, td):
        value = self.parser.tocleanstring(td)
        if value and value != '-':
            return Decimal(FrenchTransaction.clean_amount(value))
        else:
            return NotAvailable

    def submit(self):
        self.browser.select_form(nr=1)
        self.browser.submit()

    def iter_investment(self):
        for tbody in self.document.xpath(u'//table[@summary="Contenu du portefeuille valorisé"]/tbody'):

            inv = Investment()
            inv.label = self.parser.tocleanstring(tbody.xpath('./tr[1]/td[1]/a/span')[0])
            inv.code = self.parser.tocleanstring(tbody.xpath('./tr[1]/td[1]/a')[0]).split(' - ')[1]
            inv.quantity = self.parse_decimal(tbody.xpath('./tr[2]/td[2]')[0])
            inv.unitvalue = self.parse_decimal(tbody.xpath('./tr[2]/td[3]')[0])
            inv.unitprice = self.parse_decimal(tbody.xpath('./tr[2]/td[5]')[0])
            inv.valuation = self.parse_decimal(tbody.xpath('./tr[2]/td[4]')[0])
            inv.diff = self.parse_decimal(tbody.xpath('./tr[2]/td[7]')[0])

            yield inv


class LifeInsurance(MarketPage):
    def get_cons_repart(self):
        return self.document.xpath('//tr[@id="sousMenuConsultation3"]/td/div/a')[0].attrib['href']

    def iter_investment(self):
        for tr in self.document.xpath(u'//table[@class="boursedetail"]/tr[@class and not(@class="total")]'):

            inv = Investment()
            libelle = self.parser.tocleanstring(tr.xpath('./td[1]')[0]).split(' ')
            inv.label, inv.code = self.split_label_code(libelle)
            diff = self.parse_decimal(tr.xpath('./td[6]')[0])
            inv.quantity = self.parse_decimal(tr.xpath('./td[2]')[0])
            inv.unitvalue = self.parse_decimal(tr.xpath('./td[3]')[0])
            inv.unitprice = self.calc(inv.unitvalue, diff)
            inv.valuation = self.parse_decimal(tr.xpath('./td[5]')[0])
            inv.diff = self.get_diff(inv.valuation, self.calc(inv.valuation, diff))

            yield inv

    def calc(self, value, diff):
        if value is NotAvailable or diff is NotAvailable:
            return NotAvailable
        return Decimal(value) / (1 + Decimal(diff)/100)

    def get_diff(self, valuation, calc):
        if valuation is NotAvailable or calc is NotAvailable:
            return NotAvailable
        return valuation - calc

    def split_label_code(self, libelle):
        m = re.search('FR\d+', libelle[-1])
        if m:
            return ' '.join(libelle[:-1]), libelle[-1]
        else:
            return ' '.join(libelle), NotAvailable
