from __future__ import annotations
_F='license'
_E='_system'
_D='free'
_C='pro'
_B=None
_A=False
import hashlib,hmac,re
from dataclasses import dataclass
from datetime import date,datetime,timezone
from typing import Optional
from admin import config
from admin.models import db
SPC_RE=re.compile('^IVS-PRO-(?P<exp>\\d{8})-(?P<sig>[A-F0-9]{12})$',re.IGNORECASE)
@dataclass
class LicenseStatus:
	tier:str;project_count:int;project_limit:Optional[int];can_add_project:bool;expires_on:Optional[str]=_B;contact_email:str='';message:str='';spc_hint:str='';perpetual:bool=_A
	@property
	def is_pro(self):return self.tier==_C
	@property
	def is_expired_pro(self):A=self;return A.tier==_D and bool(A.message and'expired'in A.message.lower())
def _sig_for(expiry_yyyymmdd):A='utf-8';B=f"IVS-PRO|{expiry_yyyymmdd}".encode(A);C=hmac.new(config.LICENSE_SECRET.encode(A),B,hashlib.sha256).hexdigest().upper();return C[:12]
def parse_and_validate_spc(code):
	D=(code or'').strip().upper().replace(' ','');B=SPC_RE.match(D)
	if not B:return _A,'Invalid SPC format. Expected IVS-PRO-YYYYMMDD-XXXXXXXXXXXX.',_B,_A
	C=B.group('exp');E=B.group('sig').upper()
	if not hmac.compare_digest(E,_sig_for(C)):return _A,'SPC signature is invalid.',_B,_A
	try:A=datetime.strptime(C,'%Y%m%d').date()
	except ValueError:return _A,'SPC expiry date is invalid.',_B,_A
	F=datetime.now(timezone.utc).date()
	if A<F:return _A,f"This SPC expired on {A.isoformat()}. Request a new yearly license.",_B,_A
	return True,f"Valid Pro license through {A.isoformat()}.",A,_A
def activate_spc(code,*,actor='admin'):
	B=actor;C,D,A,F=parse_and_validate_spc(code)
	if not C or A is _B:raise ValueError(D)
	E=code.strip().upper().replace(' ','');db.set_license(tier=_C,spc_code=E,expires_on=A.isoformat(),activated_by=B);db.add_event(slug=_E,event_type=_F,status='success',message=f"Pro SPC activated until {A.isoformat()}",actor=B);return get_license_status()
def clear_license(*,actor='admin'):db.clear_license();db.add_event(slug=_E,event_type=_F,status='info',message='License cleared; reverted to Free tier',actor=actor);return get_license_status()
def get_license_status():
	F='expires_on';B=len(db.list_deployments());E=config.LICENSE_CONTACT_EMAIL;C=config.FREE_PROJECT_LIMIT;A=db.get_license()
	if A and A.get('tier')==_C and A.get(F):
		try:D=date.fromisoformat(A[F])
		except ValueError:D=_B
		G=datetime.now(timezone.utc).date()
		if D and D>=G:return LicenseStatus(tier=_C,project_count=B,project_limit=_B,can_add_project=True,expires_on=D.isoformat(),contact_email=E,message=f"Pro license active until {D.isoformat()}.",spc_hint=(A.get('spc_code')or'')[-4:])
		return LicenseStatus(tier=_D,project_count=B,project_limit=C,can_add_project=B<C,expires_on=A.get(F),contact_email=E,message=f"Pro license expired on {A.get(F)}. Free tier allows {C} projects. Contact {E} for a new yearly SPC.")
	return LicenseStatus(tier=_D,project_count=B,project_limit=C,can_add_project=B<C,contact_email=E,message=f"Free tier: {B}/{C} projects used.")
def assert_can_add_project():
	A=get_license_status()
	if A.can_add_project:return
	raise PermissionError(f"Free tier is limited to {config.FREE_PROJECT_LIMIT} projects. Contact {config.LICENSE_CONTACT_EMAIL} for an SPC code to unlock unlimited projects (yearly subscription).")
