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


class LaundrySessionCloseWizard(models.TransientModel):
    """Session closing wizard.

    Summarises the session's accounting figures (from account.payment / invoices)
    and asks the operator to count and enter the actual physical cash.

    On confirm:
    1. If cash_difference != 0 and post_difference_entry is True:
       → Posts a journal entry: Cash ↔ Difference Account
    2. Marks the session closed, records actual cash count.
    """
    _name = 'laundry.session.close.wizard'
    _description = 'Close Laundry Session'

    session_id = fields.Many2one(
        'laundry.session', string='Session',
        required=True, readonly=True,
    )

    # ── Session summary (read-only, from session computed fields) ─────
    opening_cash = fields.Float(
        related='session_id.opening_cash',
        string='Opening Float', readonly=True, digits=(10, 2),
    )
    total_sales = fields.Float(
        related='session_id.total_sales',
        string='Total Sales', readonly=True, digits=(10, 2),
    )
    total_cash = fields.Float(
        related='session_id.total_cash',
        string='Cash Received', readonly=True, digits=(10, 2),
    )
    total_bank = fields.Float(
        related='session_id.total_bank',
        string='Bank / Card', readonly=True, digits=(10, 2),
    )
    total_credit = fields.Float(
        related='session_id.total_credit',
        string='Outstanding / Deferred', readonly=True, digits=(10, 2),
    )
    total_paid = fields.Float(
        related='session_id.total_paid',
        string='Total Collected', readonly=True, digits=(10, 2),
    )
    outstanding_amount = fields.Float(
        related='session_id.outstanding_amount',
        string='Outstanding', readonly=True, digits=(10, 2),
    )
    expected_closing_cash = fields.Float(
        related='session_id.expected_closing_cash',
        string='Expected Cash in Drawer', readonly=True, digits=(10, 2),
    )
    order_count = fields.Integer(
        related='session_id.order_count',
        string='Orders Processed', readonly=True,
    )

    # ── Operator input ────────────────────────────────────────────────
    actual_closing_cash = fields.Float(
        string='Actual Cash Count',
        digits=(10, 2),
        help='Count the physical cash in the drawer and enter the total here.',
    )
    cash_difference = fields.Float(
        string='Difference (Actual − Expected)',
        compute='_compute_difference',
        digits=(10, 2),
    )
    post_difference_entry = fields.Boolean(
        string='Post Difference Journal Entry',
        default=True,
    )
    difference_account_id = fields.Many2one(
        'account.account',
        string='Difference Account',
        help='Account to post cash count variance against.',
    )
    closing_notes = fields.Text(string='Closing Notes')

    # ── Computed ──────────────────────────────────────────────────────
    @api.depends('actual_closing_cash', 'expected_closing_cash')
    def _compute_difference(self):
        for wiz in self:
            wiz.cash_difference = (
                wiz.actual_closing_cash - wiz.expected_closing_cash
            )

    @api.onchange('post_difference_entry')
    def _onchange_post_difference(self):
        if self.post_difference_entry and not self.difference_account_id:
            ICP = self.env['ir.config_parameter'].sudo()
            # 1. Try configured difference account from Laundry Settings
            configured_id = ICP.get_param('laundry_management.difference_account_id', '')
            if configured_id:
                try:
                    acc = self.env['account.account'].browse(int(configured_id))
                    if acc.exists():
                        self.difference_account_id = acc
                        return
                except (ValueError, TypeError):
                    pass
            # 2. Fallback: first cash-difference account on the company
            company = (
                self.session_id.company_id if self.session_id else self.env.company
            )
            acc = (
                company.default_cash_difference_expense_account_id
                or company.default_cash_difference_income_account_id
            )
            if acc:
                self.difference_account_id = acc

    # ── Confirm ───────────────────────────────────────────────────────
    def action_close(self):
        self.ensure_one()
        session = self.session_id
        if session.state != 'opened':
            raise UserError('Session is not open — cannot close.')

        diff = self.actual_closing_cash - self.expected_closing_cash
        if self.post_difference_entry and abs(diff) > 0.001:
            if not self.difference_account_id:
                raise UserError(
                    'Select a Difference Account before closing, '
                    'or uncheck "Post Difference Journal Entry".'
                )
            self._post_cash_difference_entry(session, diff)

        session.write({
            'state':               'closed',
            'closing_datetime':    fields.Datetime.now(),
            'actual_closing_cash': self.actual_closing_cash,
            'notes':               self.closing_notes or session.notes or '',
        })
        session.message_post(
            body=(
                f'Session closed by {self.env.user.name}. '
                f'Orders: {self.order_count} | '
                f'Sales: {self.total_sales:.2f} | '
                f'Cash: {self.total_cash:.2f} | '
                f'Bank: {self.total_bank:.2f} | '
                f'Actual: {self.actual_closing_cash:.2f} | '
                f'Diff: {diff:+.2f}'
            ),
        )
        return {'type': 'ir.actions.act_window_close'}

    def _post_cash_difference_entry(self, session, difference):
        """Post a journal entry for the cash count variance.

        Surplus  (difference > 0): Dr Cash  / Cr Variance Account
        Shortage (difference < 0): Dr Variance Account / Cr Cash

        Journal priority:
        1. Laundry default cash journal from Settings
        2. First cash journal for the company
        """
        company = session.company_id or self.env.company
        ICP = self.env['ir.config_parameter'].sudo()

        # 1. Try configured default from Laundry Settings
        configured_journal_id = ICP.get_param('laundry_management.cash_journal_id', '')
        cash_journal = False
        if configured_journal_id:
            try:
                cash_journal = self.env['account.journal'].browse(int(configured_journal_id))
                if not cash_journal.exists() or cash_journal.type != 'cash':
                    cash_journal = False
            except (ValueError, TypeError):
                cash_journal = False

        # 2. Fallback: first cash journal for the company
        if not cash_journal:
            cash_journal = self.env['account.journal'].search([
                ('type',       '=', 'cash'),
                ('company_id', '=', company.id),
            ], limit=1)

        if not cash_journal:
            raise UserError(
                'No Cash journal found. '
                'Configure one in Laundry → Configuration → Settings → Default Cash Journal, '
                'or in Accounting → Journals.'
            )
        cash_account = cash_journal.default_account_id
        if not cash_account:
            raise UserError(
                f'Cash journal "{cash_journal.name}" has no default account.'
            )

        amount = abs(difference)
        if difference > 0:
            debit_account, credit_account = cash_account, self.difference_account_id
        else:
            debit_account, credit_account = self.difference_account_id, cash_account

        move = self.env['account.move'].create({
            'move_type' : 'entry',
            'journal_id': cash_journal.id,
            'date'      : fields.Date.today(),
            'ref'       : f'Cash variance — {session.name}',
            'company_id': company.id,
            'line_ids': [
                (0, 0, {
                    'account_id': debit_account.id,
                    'name'      : f'Cash difference — {session.name}',
                    'debit'     : amount,
                    'credit'    : 0.0,
                }),
                (0, 0, {
                    'account_id': credit_account.id,
                    'name'      : f'Cash difference — {session.name}',
                    'debit'     : 0.0,
                    'credit'    : amount,
                }),
            ],
        })
        move.action_post()
        session.message_post(
            body=(
                f'Cash variance entry posted: '
                f'{company.currency_id.symbol}{amount:.2f} '
                f'({"surplus" if difference > 0 else "shortage"}). '
                f'Entry: {move.name}'
            ),
        )
