# -*- coding: utf-8 -*-
# Copyright 2024-2025 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)
# Part of the Fusion Claim Assistant product family.

import io
import re
import base64
import logging
import zipfile
from datetime import date, datetime

from odoo import models, fields, api, _
from odoo.exceptions import UserError

_logger = logging.getLogger(__name__)


class ADPExportRecord(models.Model):
    _name = 'fusion_claims.adp.export.record'
    _description = 'ADP Export File Record'
    _inherit = ['fusion_claims.adp.posting.schedule.mixin']
    _order = 'export_date desc, id desc'

    name = fields.Char(
        string='Filename',
        required=True,
        index=True,
    )
    export_date = fields.Datetime(
        string='Export Date',
        required=True,
        default=fields.Datetime.now,
        index=True,
    )
    posting_period_date = fields.Date(
        string='Posting Period',
        required=True,
        index=True,
        help='The ADP posting date this export targets',
    )
    posting_period_label = fields.Char(
        string='Posting Period Label',
        compute='_compute_period_fields',
        store=True,
    )
    year = fields.Char(
        string='Year',
        compute='_compute_period_fields',
        store=True,
        index=True,
    )
    month = fields.Char(
        string='Month',
        compute='_compute_period_fields',
        store=True,
    )
    month_number = fields.Integer(
        string='Month #',
        compute='_compute_period_fields',
        store=True,
        help='Numeric month for proper ordering',
    )

    file_data = fields.Binary(
        string='Export File',
        attachment=True,
        help='The exported ADP claims file (stored in filestore)',
    )
    filename = fields.Char(
        string='Download Filename',
    )
    invoice_ids = fields.Many2many(
        'account.move',
        'adp_export_record_invoice_rel',
        'export_id',
        'invoice_id',
        string='Exported Invoices',
    )
    invoice_count = fields.Integer(
        string='Invoice Count',
        compute='_compute_invoice_count',
    )
    line_count = fields.Integer(
        string='Lines Exported',
        default=0,
    )
    vendor_code = fields.Char(
        string='Vendor Code',
    )
    user_id = fields.Many2one(
        'res.users',
        string='Exported By',
        default=lambda self: self.env.uid,
        index=True,
    )
    company_id = fields.Many2one(
        'res.company',
        string='Company',
        default=lambda self: self.env.company,
        index=True,
    )
    notes = fields.Text(
        string='Notes',
    )

    @api.depends('posting_period_date')
    def _compute_period_fields(self):
        for record in self:
            ppd = record.posting_period_date
            if ppd:
                record.year = str(ppd.year)
                record.month = ppd.strftime('%B')
                record.month_number = ppd.month
                record.posting_period_label = ppd.strftime('%b %d, %Y')
            else:
                record.year = ''
                record.month = ''
                record.month_number = 0
                record.posting_period_label = ''

    @api.depends('invoice_ids')
    def _compute_invoice_count(self):
        for record in self:
            record.invoice_count = len(record.invoice_ids)

    def action_download(self):
        """Download the export file."""
        self.ensure_one()
        if not self.file_data:
            raise UserError(_("No file data available for download."))
        return {
            'type': 'ir.actions.act_url',
            'url': f'/web/content?model={self._name}&id={self.id}'
                   f'&field=file_data&filename_field=filename&download=true',
            'target': 'self',
        }

    def action_view_invoices(self):
        """Open the list of invoices included in this export."""
        self.ensure_one()
        return {
            'type': 'ir.actions.act_window',
            'name': _('Exported Invoices'),
            'res_model': 'account.move',
            'view_mode': 'list,form',
            'domain': [('id', 'in', self.invoice_ids.ids)],
        }

    def action_download_zip(self):
        """Download selected export records as a single ZIP file.

        Works as a multi-record action from the list view.
        """
        if not self:
            raise UserError(_("Please select at least one export record."))

        buf = io.BytesIO()
        with zipfile.ZipFile(buf, 'w', zipfile.ZIP_DEFLATED) as zf:
            for record in self:
                if not record.file_data:
                    continue
                file_content = base64.b64decode(record.file_data)
                archive_name = record.filename or record.name
                zf.writestr(archive_name, file_content)

        zip_data = base64.b64encode(buf.getvalue())
        buf.close()

        # Create a transient attachment for download
        today_str = date.today().strftime('%Y-%m-%d')
        zip_filename = f"ADP_Export_Files_{today_str}.zip"
        attachment = self.env['ir.attachment'].sudo().create({
            'name': zip_filename,
            'type': 'binary',
            'datas': zip_data,
            'mimetype': 'application/zip',
        })
        return {
            'type': 'ir.actions.act_url',
            'url': f'/web/content/{attachment.id}?download=true',
            'target': 'self',
        }

    # ======================================================================
    # MIGRATION: Documents app -> ADP Export Records
    # ======================================================================
    @api.model
    def migrate_from_documents(self):
        """Migrate existing ADP export files from Documents app to this model.

        Searches for binary files under 'ADP Billing Files' folder hierarchy,
        parses filenames to determine vendor code and date, calculates the
        posting period, and creates export records.

        Returns a notification dict for the UI.
        """
        if 'documents.document' not in self.env:
            return {
                'type': 'ir.actions.client',
                'tag': 'display_notification',
                'params': {
                    'title': _('Documents App Not Installed'),
                    'message': _('The Documents app is not installed. Nothing to migrate.'),
                    'type': 'warning',
                    'sticky': False,
                },
            }

        Document = self.env['documents.document'].sudo()

        root_folders = Document.search([
            ('name', 'ilike', 'ADP Billing Files'),
            ('type', '=', 'folder'),
        ])

        if not root_folders:
            return {
                'type': 'ir.actions.client',
                'tag': 'display_notification',
                'params': {
                    'title': _('No Files Found'),
                    'message': _('No "ADP Billing Files" folder found in Documents.'),
                    'type': 'info',
                    'sticky': False,
                },
            }

        all_folder_ids = self._collect_subfolder_ids(Document, root_folders.ids)
        all_folder_ids.extend(root_folders.ids)

        files = Document.search([
            ('folder_id', 'in', all_folder_ids),
            ('type', '=', 'binary'),
        ])

        if not files:
            return {
                'type': 'ir.actions.client',
                'tag': 'display_notification',
                'params': {
                    'title': _('No Files Found'),
                    'message': _('No export files found in ADP Billing Files folders.'),
                    'type': 'info',
                    'sticky': False,
                },
            }

        migrated = 0
        skipped = 0
        errors = 0

        for doc in files:
            existing = self.search([('name', '=', doc.name)], limit=1)
            if existing:
                skipped += 1
                continue

            try:
                vendor_code, file_date = self._parse_export_filename(doc.name)
                if file_date:
                    posting_date = self._get_current_posting_date(file_date)
                else:
                    posting_date = self._get_current_posting_date(
                        doc.create_date.date() if doc.create_date else date.today()
                    )

                self.create({
                    'name': doc.name,
                    'filename': doc.name,
                    'file_data': doc.datas,
                    'export_date': doc.create_date or fields.Datetime.now(),
                    'posting_period_date': posting_date,
                    'vendor_code': vendor_code or '',
                    'user_id': doc.create_uid.id if doc.create_uid else self.env.uid,
                    'company_id': self.env.company.id,
                    'notes': f'Migrated from Documents app on {date.today()}',
                })
                migrated += 1
                _logger.info("Migrated ADP export file: %s", doc.name)
            except Exception as e:
                errors += 1
                _logger.error("Failed to migrate document %s: %s", doc.name, e)

        # Archive migrated documents so they don't clutter the Documents app
        if migrated > 0:
            try:
                migrated_docs = files.filtered(
                    lambda d: not self.search([('name', '=', d.name)], limit=1) is None
                )
                if hasattr(Document, 'active'):
                    files.write({'active': False})
                    _logger.info("Archived %d documents after migration", len(files))
            except Exception as e:
                _logger.warning("Could not archive old documents: %s", e)

        msg = _(
            "Migration complete: %d files migrated, %d skipped (already exist), %d errors."
        ) % (migrated, skipped, errors)

        return {
            'type': 'ir.actions.client',
            'tag': 'display_notification',
            'params': {
                'title': _('Migration Complete'),
                'message': msg,
                'type': 'success' if errors == 0 else 'warning',
                'sticky': True,
            },
        }

    @api.model
    def _collect_subfolder_ids(self, Document, parent_ids):
        """Recursively collect all subfolder IDs under the given parent folders."""
        all_ids = []
        children = Document.search([
            ('folder_id', 'in', parent_ids),
            ('type', '=', 'folder'),
        ])
        if children:
            all_ids.extend(children.ids)
            all_ids.extend(self._collect_subfolder_ids(Document, children.ids))
        return all_ids

    @api.model
    def _parse_export_filename(self, filename):
        """Parse an ADP export filename to extract vendor code and date.

        Expected format: VENDORCODE_YYYY-MM-DD.txt
        Returns: (vendor_code, date_obj) or (None, None) if unparseable.
        """
        if not filename:
            return None, None

        match = re.match(r'^(.+?)_(\d{4}-\d{2}-\d{2})\.\w+$', filename)
        if match:
            vendor_code = match.group(1)
            try:
                file_date = date.fromisoformat(match.group(2))
                return vendor_code, file_date
            except ValueError:
                return vendor_code, None

        return None, None
