# Fusion Accounting - HTTP Controllers
# Provides web endpoints for report file generation and attachment downloads

import json

from werkzeug.exceptions import InternalServerError

from odoo import http
from odoo.addons.fusion_accounting.models.account_report import AccountReportFileDownloadException
from odoo.addons.account.controllers.download_docs import _get_headers
from odoo.http import content_disposition, request
from odoo.models import check_method_name
from odoo.tools.misc import html_escape


class AccountReportController(http.Controller):
    """Handles HTTP requests for generating and downloading
    accounting report files in various formats."""

    @http.route('/fusion_accounting', type='http', auth='user', methods=['POST'], csrf=False)
    def get_report(self, options, file_generator, **kwargs):
        """Generate a report file based on the provided options and generator method.

        :param options: JSON-encoded report configuration options
        :param file_generator: name of the method that produces the file
        :returns: HTTP response with the generated file content
        """
        current_uid = request.uid
        parsed_options = json.loads(options)

        # Determine which companies are in scope for this report
        company_ids = request.env['account.report'].get_report_company_ids(parsed_options)
        if not company_ids:
            cookie_cids = request.cookies.get('cids', str(request.env.user.company_id.id))
            company_ids = [int(cid) for cid in cookie_cids.split('-')]

        target_report = (
            request.env['account.report']
            .with_user(current_uid)
            .with_context(allowed_company_ids=company_ids)
            .browse(parsed_options['report_id'])
        )

        try:
            check_method_name(file_generator)
            file_data = target_report.dispatch_report_action(parsed_options, file_generator)

            raw_content = file_data['file_content']
            output_type = file_data['file_type']
            resp_headers = self._build_response_headers(
                output_type, file_data['file_name'], raw_content,
            )

            if output_type == 'xlsx':
                # Stream binary spreadsheet data
                http_response = request.make_response(None, headers=resp_headers)
                http_response.stream.write(raw_content)
            else:
                http_response = request.make_response(raw_content, headers=resp_headers)

            if output_type in ('zip', 'xaf'):
                # Enable streaming for large archive files to avoid
                # loading the entire content into memory at once
                http_response.direct_passthrough = True

            return http_response

        except AccountReportFileDownloadException as exc:
            if exc.content:
                exc.content['file_content'] = exc.content['file_content'].decode()
            error_payload = {
                'name': type(exc).__name__,
                'arguments': [exc.errors, exc.content],
            }
            raise InternalServerError(
                response=self._format_error_response(error_payload)
            ) from exc

        except Exception as exc:  # noqa: BLE001
            error_payload = http.serialize_exception(exc)
            raise InternalServerError(
                response=self._format_error_response(error_payload)
            ) from exc

    def _format_error_response(self, error_data):
        """Wrap error details into a JSON response matching the Odoo RPC error format."""
        envelope = {
            'code': 200,
            'message': 'Odoo Server Error',
            'data': error_data,
        }
        return request.make_response(html_escape(json.dumps(envelope)))

    def _build_response_headers(self, file_type, file_name, raw_content):
        """Construct HTTP response headers appropriate for the given file type."""
        mime_type = request.env['account.report'].get_export_mime_type(file_type)
        header_list = [
            ('Content-Type', mime_type),
            ('Content-Disposition', content_disposition(file_name)),
        ]

        # Include Content-Length for text-based formats
        if file_type in ('xml', 'txt', 'csv', 'kvr'):
            header_list.append(('Content-Length', len(raw_content)))

        return header_list

    @http.route(
        '/fusion_accounting/download_attachments/<models("ir.attachment"):attachments>',
        type='http',
        auth='user',
    )
    def download_report_attachments(self, attachments):
        """Download one or more report attachments, packaging them
        into a zip archive when multiple files are requested."""
        attachments.check_access('read')
        assert all(
            att.res_id and att.res_model == 'res.partner'
            for att in attachments
        )

        if len(attachments) == 1:
            single = attachments
            resp_headers = _get_headers(single.name, single.mimetype, single.raw)
            return request.make_response(single.raw, resp_headers)
        else:
            zip_data = attachments._build_zip_from_attachments()
            resp_headers = _get_headers('attachments.zip', 'zip', zip_data)
            return request.make_response(zip_data, resp_headers)
