from copy import copy

from django.db import models, transaction
from django.db.models import Max
from datetime import datetime


def generate_agreement_number():
    """Generate unique agreement number with select_for_update."""
    from agreements.models import Agreement
    year = datetime.now().year
    prefix = f"AGR-{year}-"
    with transaction.atomic():
        last = (
            Agreement.objects
            .select_for_update()
            .filter(agreement_no__startswith=prefix)
            .order_by("-agreement_no")
            .first()
        )
        if last:
            parts = last.agreement_no.split('-')
            last_num = int(parts[-1]) if parts[-1].isdigit() else 0
            new_num = last_num + 1
        else:
            new_num = 1
        return f"{prefix}{new_num:04d}"


def validate_timeline_overlap(phases):
    """
    Return True if no overlap.
    Allows one phase to end on the same day the next phase starts.
    """
    sorted_phases = sorted(phases, key=lambda x: x["start_day"])

    for i in range(len(sorted_phases) - 1):
        if sorted_phases[i]["end_day"] > sorted_phases[i + 1]["start_day"]:
            return False

    return True


def clone_agreement(original, new_version=False, new_title=None, user=None):
    """
    Deep clone an agreement safely without mutating the original instance.
    Returns the new agreement instance.
    """
    from agreements.models import (
        Agreement, ScopeNode, ResponsibilityNode, TimelinePhase,
        PaymentSection, PaymentMilestone, Annexure, AgreementTerms,
        CustomSection, Signature, AgreementAttachment, AgreementHistory
    )

    with transaction.atomic():
        # 1. Copy agreement fields
        old_pk = original.pk
        original_dict = {
            'title': new_title or original.title,
            'agreement_date': original.agreement_date,
            'referred_by': original.referred_by,
            'agency': original.agency,
            'principal': original.principal,
            'purpose': original.purpose,
            'status': 'DRAFT',
            'template_used': original.template_used,
        }
        if new_version:
            original_dict['version_number'] = original.version_number + 1
            original_dict['is_latest'] = True
            original_dict['parent_agreement'] = original
            original_dict['agreement_no'] = original.agreement_no  # same number
        else:
            original_dict['version_number'] = 1
            original_dict['is_latest'] = True
            original_dict['parent_agreement'] = None
            original_dict['agreement_no'] = generate_agreement_number()

        new_agreement = Agreement.objects.create(**original_dict)
        new_id = new_agreement.pk

        # 2. Clone ScopeNodes (recursive) — copy.copy() avoids mutating
        # the original in-memory objects while still carrying over every
        # field automatically (no field list to maintain by hand).
        def clone_scope(node, new_parent=None):
            children = list(node.children.all())
            new_node = copy(node)
            new_node.pk = None
            new_node.id = None
            new_node.agreement_id = new_id
            new_node.parent = new_parent
            new_node.save()
            for child in children:
                clone_scope(child, new_node)

        for root in ScopeNode.objects.filter(agreement_id=old_pk, parent__isnull=True):
            clone_scope(root)

        # 3. Clone ResponsibilityNodes
        def clone_resp(node, new_parent=None):
            children = list(node.children.all())
            new_node = copy(node)
            new_node.pk = None
            new_node.id = None
            new_node.agreement_id = new_id
            new_node.parent = new_parent
            new_node.save()
            for child in children:
                clone_resp(child, new_node)

        for root in ResponsibilityNode.objects.filter(agreement_id=old_pk, parent__isnull=True):
            clone_resp(root)

        # 4. Clone TimelinePhases
        for phase in TimelinePhase.objects.filter(agreement_id=old_pk):
            new_phase = copy(phase)
            new_phase.pk = None
            new_phase.id = None
            new_phase.agreement_id = new_id
            new_phase.save()

        # 5. Clone PaymentSection + Milestones
        try:
            old_payment = PaymentSection.objects.get(agreement_id=old_pk)
        except PaymentSection.DoesNotExist:
            old_payment = None

        if old_payment:
            milestones = list(old_payment.milestones.all())
            new_payment = copy(old_payment)
            new_payment.pk = None
            new_payment.id = None
            new_payment.agreement_id = new_id
            new_payment.finalized = False
            new_payment.save()

            for m in milestones:
                new_m = copy(m)
                new_m.pk = None
                new_m.id = None
                new_m.payment_id = new_payment.pk
                new_m.save()

        # 6. Clone Annexure
        try:
            old_annexure = Annexure.objects.get(agreement_id=old_pk)
        except Annexure.DoesNotExist:
            old_annexure = None

        if old_annexure:
            new_annexure = copy(old_annexure)
            new_annexure.pk = None
            new_annexure.id = None
            new_annexure.agreement_id = new_id
            new_annexure.save()

        # 7. Clone AgreementTerms
        for at in AgreementTerms.objects.filter(agreement_id=old_pk):
            new_at = copy(at)
            new_at.pk = None
            new_at.id = None
            new_at.agreement_id = new_id
            new_at.save()

        # 8. Clone CustomSections
        for cs in CustomSection.objects.filter(agreement_id=old_pk):
            new_cs = copy(cs)
            new_cs.pk = None
            new_cs.id = None
            new_cs.agreement_id = new_id
            new_cs.save()

        # 9. Attachments are intentionally not duplicated.
        # The new agreement starts without attachments — files are not
        # copied and attachment records are not re-pointed at the old
        # file, to avoid two agreements silently sharing one file object.
        # If you later want to reuse (not copy) the underlying files:
        #
        # for att in AgreementAttachment.objects.filter(agreement_id=old_pk):
        #     AgreementAttachment.objects.create(
        #         agreement=new_agreement,
        #         file=att.file,
        #         description=att.description,
        #     )

        # 10. Clone Signature
        try:
            old_sig = Signature.objects.get(agreement_id=old_pk)
        except Signature.DoesNotExist:
            old_sig = None

        if old_sig:
            new_sig = copy(old_sig)
            new_sig.pk = None
            new_sig.id = None
            new_sig.agreement_id = new_id
            new_sig.signed_date = None
            new_sig.ip_address = None
            new_sig.signed_time = None
            # Only reset if the field actually exists on your model —
            # confirm the field name/values and remove this guard once known.
            if hasattr(new_sig, 'status'):
                new_sig.status = 'PENDING'
            new_sig.save()

        # 11. Log creation
        AgreementHistory.objects.create(
            agreement=new_agreement,
            action='CREATED',
            user=user,
            details={'cloned_from': old_pk}
        )

        return new_agreement