# -*- coding: utf-8 -*-
"""STEP 12 — File archive + retention lifecycle + calendar layers.

Three concerns:
  * ARCHIVE — metadata over ir.attachment with a SHA-256 checksum and a retention
    class. Content lives behind ir.attachment (MinIO/Synology in production).
  * RETENTION — attorney-configured retention classes drive a closed-matter
    destruction lifecycle: retained -> eligible -> destroyed. The audit trail records
    THAT destruction happened (and the checksum as proof) WITHOUT retaining the
    destroyed content. Plus client-file return at matter close.
  * CALENDAR LAYERS — a layer tag on deadlines so the calendar can be filtered by
    category (statutory / hearing / internal).

VERIFY current rule — Florida file-retention obligations are volatile; retention
periods must be confirmed and configured by the attorney, not hardcoded.
"""

import base64
import hashlib

from dateutil.relativedelta import relativedelta

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

ATTORNEY_GROUP = "activeblue_familylaw_v2.group_familylaw_attorney"


class FamilyLawRetentionClass(models.Model):
    _name = "familylaw.retention.class"
    _description = "Retention Class"
    _order = "name"

    name = fields.Char(required=True)
    code = fields.Char()
    retention_years = fields.Integer(
        string="Retain (years)", required=True, default=6,
        help="Years to retain after archiving / matter close. Attorney-configured; "
             "confirm the current Florida obligation.",
    )
    description = fields.Text()
    active = fields.Boolean(default=True)


class FamilyLawArchive(models.Model):
    _name = "familylaw.archive"
    _description = "Archived File (with retention)"
    _inherit = ["mail.thread", "mail.activity.mixin"]
    _order = "date_archived desc"

    name = fields.Char(required=True, tracking=True)
    case_id = fields.Many2one("familylaw.case", index=True, tracking=True)
    proceeding_id = fields.Many2one("familylaw.proceeding", index=True)
    document_id = fields.Many2one("familylaw.document", index=True)
    attachment_id = fields.Many2one("ir.attachment", string="File", ondelete="set null")

    checksum = fields.Char(
        compute="_compute_checksum", store=True, string="SHA-256",
        help="SHA-256 of the file content; kept as proof even after destruction.",
    )
    retention_class_id = fields.Many2one("familylaw.retention.class",
                                         string="Retention Class", tracking=True)
    date_archived = fields.Date(default=fields.Date.context_today, tracking=True)
    destruction_eligible_date = fields.Date(
        compute="_compute_eligible_date", store=True,
    )
    is_destruction_eligible = fields.Boolean(
        compute="_compute_is_eligible", search="_search_is_eligible",
        string="Eligible Now?",
    )
    retention_state = fields.Selection(
        selection=[
            ("retained", "Retained"),
            ("eligible", "Eligible for Destruction"),
            ("destroyed", "Destroyed"),
        ],
        default="retained",
        required=True,
        tracking=True,
    )
    destroyed_date = fields.Datetime(readonly=True, copy=False)
    destroyed_by_id = fields.Many2one("res.users", readonly=True, copy=False)

    @api.depends("attachment_id", "attachment_id.datas")
    def _compute_checksum(self):
        for a in self:
            if a.attachment_id and a.attachment_id.datas:
                raw = base64.b64decode(a.attachment_id.datas)
                a.checksum = hashlib.sha256(raw).hexdigest()
            elif not a.checksum:  # preserve checksum-as-proof after destruction
                a.checksum = False

    @api.depends("date_archived", "retention_class_id.retention_years")
    def _compute_eligible_date(self):
        for a in self:
            if a.date_archived and a.retention_class_id:
                a.destruction_eligible_date = a.date_archived + relativedelta(
                    years=a.retention_class_id.retention_years)
            else:
                a.destruction_eligible_date = False

    @api.depends("destruction_eligible_date", "retention_state")
    def _compute_is_eligible(self):
        today = fields.Date.context_today(self)
        for a in self:
            a.is_destruction_eligible = bool(
                a.retention_state == "retained"
                and a.destruction_eligible_date
                and a.destruction_eligible_date <= today
            )

    def _search_is_eligible(self, operator, value):
        if operator not in ("=", "!="):
            raise ValueError(_("Unsupported operator for is_destruction_eligible."))
        today = fields.Date.context_today(self)
        want = (operator == "=" and value) or (operator == "!=" and not value)
        if want:
            return [
                ("retention_state", "=", "retained"),
                ("destruction_eligible_date", "!=", False),
                ("destruction_eligible_date", "<=", today),
            ]
        return [
            "|", "|",
            ("retention_state", "!=", "retained"),
            ("destruction_eligible_date", "=", False),
            ("destruction_eligible_date", ">", today),
        ]

    def _ensure_attorney(self):
        if not self.env.user.has_group(ATTORNEY_GROUP):
            raise UserError(_("Only an attorney may authorize destruction."))

    def action_destroy(self):
        """Destroy the archived content. Records THAT destruction happened (with the
        checksum as proof) but does NOT retain the destroyed content."""
        self._ensure_attorney()
        for a in self:
            if a.retention_state == "destroyed":
                continue
            a.message_post(body=_(
                "Archived content DESTROYED per retention class '%(rc)s'. "
                "Checksum retained as proof: %(cs)s. Content not retained.",
                rc=a.retention_class_id.name or _("(none)"),
                cs=a.checksum or _("(n/a)")))
            if a.attachment_id:
                a.attachment_id.sudo().unlink()
            a.write({
                "retention_state": "destroyed",
                "destroyed_date": fields.Datetime.now(),
                "destroyed_by_id": self.env.user.id,
            })
        return True

    @api.model
    def _cron_flag_eligible(self):
        today = fields.Date.context_today(self)
        eligible = self.search([
            ("retention_state", "=", "retained"),
            ("destruction_eligible_date", "<=", today),
            ("destruction_eligible_date", "!=", False),
        ])
        for a in eligible:
            a.retention_state = "eligible"
            a.message_post(body=_("Now eligible for destruction (retention period elapsed)."))
        return len(eligible)


class FamilyLawCaseRetention(models.Model):
    _inherit = "familylaw.case"

    archive_ids = fields.One2many("familylaw.archive", "case_id", string="Archive")
    client_file_returned = fields.Boolean(
        string="Client File Returned", readonly=True, copy=False, tracking=True,
    )
    client_file_returned_date = fields.Date(readonly=True, copy=False)

    def action_return_client_file(self):
        """Record return of the client file at matter close (attorney-only)."""
        self._ensure_attorney()
        for case in self:
            if case.state != "closed":
                raise UserError(
                    _("The client file is returned at matter close. Close the matter "
                      "first."))
            case.client_file_returned = True
            case.client_file_returned_date = fields.Date.context_today(case)
            case.message_post(body=_("Client file returned to the client."))
        return True


class FamilyLawDeadlineLayer(models.Model):
    _inherit = "familylaw.deadline"

    layer = fields.Selection(
        selection=[
            ("statutory", "Statutory / Procedural"),
            ("hearing", "Hearing"),
            ("internal", "Internal / Office"),
        ],
        default="statutory",
        string="Calendar Layer",
        help="Categorises the deadline so the calendar can be filtered by layer.",
    )
