# -*- coding: utf-8 -*-
# Copyright 2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)

import base64
import re
import time
import logging
from datetime import datetime, timedelta

from odoo import api, fields, models, _

_logger = logging.getLogger(__name__)

RC_RATE_LIMIT_DELAY = 6


class FusionFaxRC(models.Model):
    """Extend fusion.fax with contact matching, smart buttons,
    forward fax, and send-new-fax actions."""

    _inherit = 'fusion.fax'

    invoice_count = fields.Integer(
        string='Invoices', compute='_compute_partner_counts',
    )
    sale_order_count = fields.Integer(
        string='Sales Orders', compute='_compute_partner_counts',
    )

    @api.depends('partner_id')
    def _compute_partner_counts(self):
        for rec in self:
            if rec.partner_id:
                partner = rec.partner_id.commercial_partner_id or rec.partner_id
                partner_ids = (partner | partner.child_ids).ids
                rec.sale_order_count = self.env['sale.order'].search_count(
                    [('partner_id', 'in', partner_ids)],
                )
                rec.invoice_count = self.env['account.move'].search_count(
                    [('partner_id', 'in', partner_ids),
                     ('move_type', 'in', ('out_invoice', 'out_refund'))],
                )
            else:
                rec.sale_order_count = 0
                rec.invoice_count = 0

    @api.model_create_multi
    def create(self, vals_list):
        for vals in vals_list:
            if not vals.get('partner_id'):
                partner = self._match_fax_partner(
                    vals.get('fax_number', ''),
                    vals.get('sender_number', ''),
                    vals.get('direction', ''),
                )
                if partner:
                    vals['partner_id'] = partner.id
        return super().create(vals_list)

    @api.model
    def _match_fax_partner(self, fax_number, sender_number, direction):
        """Match a fax to a contact by fax number, phone, or mobile."""
        search_number = sender_number if direction == 'inbound' else fax_number
        if not search_number:
            return False

        cleaned = self._normalize_fax_phone(search_number)
        if not cleaned or len(cleaned) < 7:
            return False

        Partner = self.env['res.partner']

        if 'x_ff_fax_number' in Partner._fields:
            partners = Partner.search(
                [('x_ff_fax_number', '!=', False)],
                order='customer_rank desc, write_date desc',
            )
            for p in partners:
                if self._normalize_fax_phone(p.x_ff_fax_number) == cleaned:
                    return p

        phone_fields = [f for f in ('phone', 'mobile') if f in Partner._fields]
        if phone_fields:
            domain = ['|'] * (len(phone_fields) - 1)
            for f in phone_fields:
                domain.append((f, '!=', False))
            partners = Partner.search(domain, order='customer_rank desc, write_date desc')
            for p in partners:
                for f in phone_fields:
                    val = p[f]
                    if val and self._normalize_fax_phone(val) == cleaned:
                        return p

        return False

    @staticmethod
    def _normalize_fax_phone(number):
        return re.sub(r'\D', '', number or '')[-10:] if number else ''

    # ──────────────────────────────────────────────────────────
    # Smart button actions
    # ──────────────────────────────────────────────────────────

    def action_view_contact(self):
        self.ensure_one()
        if not self.partner_id:
            return
        return {
            'type': 'ir.actions.act_window',
            'res_model': 'res.partner',
            'res_id': self.partner_id.id,
            'view_mode': 'form',
            'target': 'current',
        }

    def action_view_sale_orders(self):
        self.ensure_one()
        if not self.partner_id:
            return
        partner = self.partner_id.commercial_partner_id or self.partner_id
        partner_ids = (partner | partner.child_ids).ids
        return {
            'type': 'ir.actions.act_window',
            'name': _('Sales Orders - %s') % self.partner_id.name,
            'res_model': 'sale.order',
            'view_mode': 'list,form',
            'domain': [('partner_id', 'in', partner_ids)],
            'target': 'current',
        }

    def action_view_invoices(self):
        self.ensure_one()
        if not self.partner_id:
            return
        partner = self.partner_id.commercial_partner_id or self.partner_id
        partner_ids = (partner | partner.child_ids).ids
        return {
            'type': 'ir.actions.act_window',
            'name': _('Invoices - %s') % self.partner_id.name,
            'res_model': 'account.move',
            'view_mode': 'list,form',
            'domain': [('partner_id', 'in', partner_ids),
                        ('move_type', 'in', ('out_invoice', 'out_refund'))],
            'target': 'current',
        }

    # ──────────────────────────────────────────────────────────
    # Forward Fax (received fax -> send to someone else)
    # ──────────────────────────────────────────────────────────

    def action_forward_fax(self):
        """Open Send Fax wizard with this fax's documents pre-attached."""
        self.ensure_one()
        attachment_ids = self.document_ids.sorted('sequence').mapped('attachment_id').ids
        ctx = {
            'default_document_source_fax_id': self.id,
            'forward_attachment_ids': attachment_ids,
        }
        return {
            'type': 'ir.actions.act_window',
            'name': _('Forward Fax - %s') % self.name,
            'res_model': 'fusion_faxes.send.fax.wizard',
            'view_mode': 'form',
            'target': 'new',
            'context': ctx,
        }

    # ──────────────────────────────────────────────────────────
    # Send New Fax (pre-fill contact from current fax)
    # ──────────────────────────────────────────────────────────

    def action_send_new_fax(self):
        """Open Send Fax wizard pre-filled with this fax's contact info."""
        self.ensure_one()
        ctx = {}
        if self.partner_id:
            ctx['default_partner_id'] = self.partner_id.id
        fax_num = ''
        if self.direction == 'inbound':
            fax_num = self.sender_number or self.fax_number
        else:
            fax_num = self.fax_number
        if fax_num:
            ctx['default_fax_number'] = fax_num
        return {
            'type': 'ir.actions.act_window',
            'name': _('Send New Fax'),
            'res_model': 'fusion_faxes.send.fax.wizard',
            'view_mode': 'form',
            'target': 'new',
            'context': ctx,
        }

    # ------------------------------------------------------------------
    # Historical fax import (via rc.config OAuth)
    # ------------------------------------------------------------------

    @api.model
    def _run_historical_fax_import(self):
        """Background job: import up to 12 months of faxes in monthly chunks."""
        config = self.env['rc.config']._get_active_config()
        if not config:
            _logger.warning("RC Fax Historical Import: No connected config.")
            return

        try:
            config._ensure_token()
        except Exception:
            _logger.exception("RC Fax Historical Import: Token invalid.")
            return

        ICP = self.env['ir.config_parameter'].sudo()
        now = datetime.utcnow()
        total_imported = 0
        total_skipped = 0

        for months_back in range(12, 0, -1):
            chunk_start = now - timedelta(days=months_back * 30)
            chunk_end = now - timedelta(days=(months_back - 1) * 30)
            chunk_num = 13 - months_back

            date_from = chunk_start.strftime('%Y-%m-%dT%H:%M:%S.000Z')
            date_to = chunk_end.strftime('%Y-%m-%dT%H:%M:%S.000Z')

            chunk_key = f'fusion_rc.fax_import_done_{chunk_start.strftime("%Y%m")}'
            if ICP.get_param(chunk_key, ''):
                _logger.info(
                    "RC Fax Import [%d/12]: %s to %s -- already done, skipping.",
                    chunk_num, date_from[:10], date_to[:10],
                )
                total_skipped += 1
                continue

            _logger.info(
                "RC Fax Import [%d/12]: %s to %s ...",
                chunk_num, date_from[:10], date_to[:10],
            )
            try:
                chunk_count = self._sync_faxes_from_rc(config, date_from, date_to)
                ICP.set_param(chunk_key, 'done')
                total_imported += chunk_count
                _logger.info(
                    "RC Fax Import [%d/12]: imported %d faxes.",
                    chunk_num, chunk_count,
                )
            except Exception:
                _logger.exception(
                    "RC Fax Import [%d/12]: chunk failed, will retry next run.",
                    chunk_num,
                )

        _logger.info(
            "RC Fax Historical Import complete: %d imported, %d chunks skipped.",
            total_imported, total_skipped,
        )

    def _sync_faxes_from_rc(self, config, date_from, date_to):
        """Fetch faxes (inbound + outbound) from RC message-store via OAuth."""
        imported = 0

        for direction in ('Inbound', 'Outbound'):
            params = {
                'messageType': 'Fax',
                'direction': direction,
                'dateFrom': date_from,
                'dateTo': date_to,
                'perPage': 100,
            }
            endpoint = '/restapi/v1.0/account/~/extension/~/message-store'

            while endpoint:
                time.sleep(RC_RATE_LIMIT_DELAY)
                resp = config._api_request('GET', endpoint, params=params)
                data = resp.json() if hasattr(resp, 'json') else resp

                records = data.get('records', []) if isinstance(data, dict) else []

                for msg in records:
                    msg_id = str(msg.get('id', ''))
                    if not msg_id:
                        continue
                    if self.search_count([('ringcentral_message_id', '=', msg_id)]):
                        continue

                    self._import_fax_from_rc(msg, config, direction.lower())
                    imported += 1

                params = None
                nav = data.get('navigation', {}) if isinstance(data, dict) else {}
                next_page = nav.get('nextPage', {}) if isinstance(nav, dict) else {}
                next_uri = next_page.get('uri', '') if isinstance(next_page, dict) else ''
                endpoint = next_uri or None

        return imported

    def _import_fax_from_rc(self, msg, config, direction):
        """Import a single fax message from RingCentral API response."""
        try:
            msg_id = str(msg.get('id', ''))
            from_info = msg.get('from', {}) or {}
            to_info = msg.get('to', [{}])
            if isinstance(to_info, list) and to_info:
                to_info = to_info[0]
            elif not isinstance(to_info, dict):
                to_info = {}

            sender = from_info.get('phoneNumber', '')
            recipient = to_info.get('phoneNumber', '')
            creation_time = msg.get('creationTime', '')
            read_status = msg.get('readStatus', '')
            page_count = msg.get('faxPageCount', 0)
            attachments = msg.get('attachments', [])

            received_dt = False
            if creation_time:
                try:
                    clean = creation_time.replace('Z', '+00:00')
                    received_dt = datetime.fromisoformat(clean).strftime(
                        '%Y-%m-%d %H:%M:%S'
                    )
                except (ValueError, AttributeError):
                    pass

            fax_number = recipient if direction == 'outbound' else sender
            search_number = sender if direction == 'inbound' else recipient
            partner = self._match_fax_partner(
                fax_number, search_number, direction
            )

            document_lines = []
            for att in attachments:
                att_uri = att.get('uri', '')
                att_type = att.get('contentType', '')
                if not att_uri or 'text' in (att_type or ''):
                    continue

                try:
                    time.sleep(RC_RATE_LIMIT_DELAY)
                    resp = config._api_request('GET', att_uri)
                    pdf_content = resp.content if hasattr(resp, 'content') else resp
                    if not pdf_content:
                        continue

                    file_ext = 'pdf' if 'pdf' in (att_type or '') else 'bin'
                    file_name = (
                        f'FAX_{"IN" if direction == "inbound" else "OUT"}'
                        f'_{msg_id}.{file_ext}'
                    )

                    ir_att = self.env['ir.attachment'].sudo().create({
                        'name': file_name,
                        'type': 'binary',
                        'datas': base64.b64encode(
                            pdf_content
                            if isinstance(pdf_content, bytes)
                            else pdf_content.encode()
                        ),
                        'mimetype': att_type or 'application/pdf',
                        'res_model': 'fusion.fax',
                    })
                    document_lines.append((0, 0, {
                        'sequence': 10,
                        'attachment_id': ir_att.id,
                    }))
                except Exception:
                    _logger.exception(
                        "RC Fax Import: Failed to download attachment for %s",
                        msg_id,
                    )

            state = 'received' if direction == 'inbound' else 'sent'

            self.sudo().create({
                'direction': direction,
                'state': state,
                'fax_number': fax_number or 'Unknown',
                'sender_number': sender if direction == 'inbound' else False,
                'partner_id': partner.id if partner else False,
                'ringcentral_message_id': msg_id,
                'received_date': received_dt if direction == 'inbound' else False,
                'sent_date': received_dt if direction == 'outbound' else False,
                'page_count': page_count or 0,
                'rc_read_status': read_status,
                'document_ids': document_lines,
            })

        except Exception:
            _logger.exception(
                "RC Fax Import: Failed to import fax %s", msg.get('id', '?')
            )
