from __future__ import annotations
from datetime import date, datetime, timedelta, timezone
from calendar import monthrange
from sqlalchemy import and_, or_, select
from sqlalchemy.orm import Session
from app.modules.alerts.service import create_alert
from app.modules.clients.models import Client
from app.modules.consultants.models import ConsultantProfile
from app.modules.registrations.models import (
    RegistrationType, ClientRegistration, ClientRelatedPerson, ClientDigitalSignature,
    RegistrationLifecycleRule, RegistrationDocumentTemplate, RegistrationRenewalTask,
    RegistrationRenewalTaskDocument, ClientRegistrationAction,
)

DEFAULT_TYPES = [
    ("PAN","PAN","Income Tax Department",False,False),
    ("TAN","TAN","Income Tax Department",False,False),
    ("GSTIN","GST Registration","GST Department",False,False),
    ("CIN","Company Registration","Ministry of Corporate Affairs",False,False),
    ("LLPIN","LLP Registration","Ministry of Corporate Affairs",False,False),
    ("DSC","Digital Signature Certificate","Certifying Authority",True,True),
    ("UDYAM","Udyam / MSME Registration","Ministry of MSME",False,False),
    ("FSSAI","FSSAI Licence","FSSAI",True,False),
    ("IEC","Import Export Code","DGFT",False,False),
    ("SHOP_ACT","Shops and Establishment","State Authority",True,False),
    ("TRADE_LICENCE","Trade Licence","Local Authority",True,False),
    ("PT","Professional Tax","State Authority",True,False),
    ("PF","EPFO Registration","EPFO",False,False),
    ("ESI","ESIC Registration","ESIC",False,False),
    ("12AB","Section 12AB Registration","Income Tax Department",True,False),
    ("80G","Section 80G Approval","Income Tax Department",True,False),
    ("FCRA","FCRA Registration","Ministry of Home Affairs",True,False),
    ("RERA","RERA Registration","RERA Authority",True,False),
]

def seed_registration_types(db: Session) -> None:
    existing={x for x in db.execute(select(RegistrationType.code)).scalars().all()}
    for i,(code,name,authority,expiry,person) in enumerate(DEFAULT_TYPES,1):
        if code not in existing:
            db.add(RegistrationType(code=code,name=name,issuing_authority=authority,supports_expiry=expiry,supports_related_person=person,sort_order=i*10))
    db.commit()

def add_months(value: date, months: int) -> date:
    m=value.month-1+months; y=value.year+m//12; m=m%12+1
    return date(y,m,min(value.day,monthrange(y,m)[1]))

def resolve_next_action(reg: ClientRegistration, rule: RegistrationLifecycleRule|None) -> date|None:
    if reg.valid_until and rule and rule.cycle_type=="expiry_renewal":
        return reg.valid_until-timedelta(days=max(0,rule.renewal_window_days))
    base=reg.effective_from or reg.issue_date
    if base and rule and rule.frequency_months:
        candidate=add_months(base,rule.frequency_months)
        while candidate<=date.today(): candidate=add_months(candidate,rule.frequency_months)
        return candidate
    return reg.next_action_date

def create_task_documents(db: Session, task: RegistrationRenewalTask, registration_type_id: int) -> None:
    rows=db.execute(select(RegistrationDocumentTemplate).where(RegistrationDocumentTemplate.registration_type_id==registration_type_id,RegistrationDocumentTemplate.action_type==task.action_type,RegistrationDocumentTemplate.is_active.is_(True)).order_by(RegistrationDocumentTemplate.sort_order)).scalars().all()
    for row in rows:
        db.add(RegistrationRenewalTaskDocument(renewal_task_id=task.id,template_id=row.id,document_name=row.document_name,mandatory=row.mandatory))

def _alert_once(db: Session,user_id:int|None,title:str,message:str,target_url:str,tenant_id:int,branch_id:int|None):
    if not user_id:return
    today=datetime.now(timezone.utc).date()
    from app.modules.alerts.models import UserAlert
    exists=db.execute(select(UserAlert.id).where(UserAlert.user_id==user_id,UserAlert.title==title,UserAlert.target_url==target_url,UserAlert.created_at_utc>=datetime.combine(today,datetime.min.time(),tzinfo=timezone.utc))).scalar_one_or_none()
    if not exists:create_alert(db,user_id=user_id,title=title,message=message,tenant_id=tenant_id,branch_id=branch_id,role_context="registrations",alert_type="registration_expiry",priority="high",target_url=target_url,commit=False)

def run_registration_lifecycle_once(db: Session, today: date|None=None) -> dict:
    today=today or date.today(); created=alerts=0
    regs=db.execute(select(ClientRegistration).where(ClientRegistration.status.in_(["active","renewal_due","update_due"]))).scalars().all()
    for reg in regs:
        rule=db.get(RegistrationLifecycleRule,reg.lifecycle_rule_id) if reg.lifecycle_rule_id else None
        if not rule:
            rule=db.execute(select(RegistrationLifecycleRule).join(Client, Client.id==reg.client_id).where(RegistrationLifecycleRule.registration_type_id==reg.registration_type_id,RegistrationLifecycleRule.is_active.is_(True),or_(RegistrationLifecycleRule.tenant_id.is_(None),RegistrationLifecycleRule.tenant_id==reg.tenant_id),or_(RegistrationLifecycleRule.entity_type.is_(None),RegistrationLifecycleRule.entity_type==Client.client_type)).order_by(RegistrationLifecycleRule.tenant_id.desc(),RegistrationLifecycleRule.id.desc())).scalars().first()
        due=reg.valid_until or reg.next_action_date
        if not due: continue
        next_action=resolve_next_action(reg,rule)
        if next_action and reg.next_action_date!=next_action: reg.next_action_date=next_action
        window=(rule.renewal_window_days if rule else 60)
        if due-timedelta(days=window)<=today and reg.auto_create_task:
            task=db.execute(select(RegistrationRenewalTask).where(RegistrationRenewalTask.registration_id==reg.id,RegistrationRenewalTask.cycle_due_date==due)).scalar_one_or_none()
            if not task:
                rt=db.get(RegistrationType,reg.registration_type_id); client=db.get(Client,reg.client_id)
                action="renewal" if reg.valid_until else "update"
                task=RegistrationRenewalTask(tenant_id=reg.tenant_id,branch_id=reg.branch_id,client_id=reg.client_id,registration_id=reg.id,lifecycle_rule_id=rule.id if rule else None,cycle_due_date=due,action_type=action,title=f"{action.title()} - {rt.name} - {client.client_name}",priority="critical" if due<=today else "high",assigned_user_id=reg.assigned_user_id,assigned_consultant_id=reg.assigned_consultant_id,client_approval_required=bool(rule and rule.requires_client_approval),partner_review_required=bool(rule and rule.requires_partner_review))
                db.add(task);db.flush();create_task_documents(db,task,reg.registration_type_id);created+=1
            client=db.get(Client,reg.client_id)
            days=(due-today).days
            title=f"{db.get(RegistrationType,reg.registration_type_id).name} {'expired' if days<0 else 'due'}: {client.client_name}"
            msg=f"Registration {reg.registration_number} is {'overdue by '+str(abs(days))+' day(s)' if days<0 else 'due in '+str(days)+' day(s)'} on {due:%d-%m-%Y}."
            reminder_days={int(x.strip()) for x in ((rule.reminder_days_csv if rule else "60,30,15,7,0") or "").split(",") if x.strip().lstrip("-").isdigit()}
            should_alert = days in reminder_days or days < 0
            if should_alert:
                _alert_once(db,reg.assigned_user_id or client.partner_id,title,msg,f"/registrations/tasks/{task.id}",reg.tenant_id,reg.branch_id);alerts+=1
            if should_alert and reg.assigned_consultant_id:
                cp=db.get(ConsultantProfile,reg.assigned_consultant_id); _alert_once(db,cp.user_id if cp else None,title,msg,f"/registrations/tasks/{task.id}",reg.tenant_id,reg.branch_id)
            reg.status="expired" if due<today else ("renewal_due" if reg.valid_until else "update_due")
    db.commit();return {"tasks_created":created,"alerts_processed":alerts}

def complete_renewal(db:Session, task:RegistrationRenewalTask, *, new_valid_until:date|None, acknowledgement_number:str|None, actor_user_id:int, notes:str|None):
    reg=db.get(ClientRegistration,task.registration_id); previous=reg.valid_until
    task.status="completed"; task.completed_on=date.today(); task.acknowledgement_number=acknowledgement_number; task.notes=notes
    if new_valid_until: reg.valid_until=new_valid_until
    rt=db.get(RegistrationType,reg.registration_type_id)
    if rt and rt.code=="DSC" and new_valid_until:
        dsc=db.execute(select(ClientDigitalSignature).where(ClientDigitalSignature.client_id==reg.client_id,ClientDigitalSignature.related_person_id==reg.related_person_id).order_by(ClientDigitalSignature.id.desc())).scalars().first()
        if dsc:
            dsc.expires_on=new_valid_until; dsc.status="active"
    rule=db.get(RegistrationLifecycleRule,task.lifecycle_rule_id) if task.lifecycle_rule_id else None
    reg.status="active"; reg.next_action_date=resolve_next_action(reg,rule)
    db.add(ClientRegistrationAction(tenant_id=reg.tenant_id,client_id=reg.client_id,registration_id=reg.id,renewal_task_id=task.id,action_type=task.action_type,previous_valid_until=previous,new_valid_until=new_valid_until,submission_date=task.submitted_on,acknowledgement_number=acknowledgement_number,completion_date=date.today(),status="completed",performed_by_user_id=actor_user_id,notes=notes))
    db.commit()
