#!/usr/bin/env python3
"""Generate Jobnixy admin models, controllers, and core app files."""
from pathlib import Path

ROOT = Path("/Applications/XAMPP/xamppfiles/htdocs/jobnixy")

# ---------- Shared Models in App\Models (shared domain) ----------
MODELS = {
    "UserProfile": {
        "table": "user_profiles",
        "fillable": ["user_id","first_name","last_name","gender","date_of_birth","bio","city","state","address_line","latitude","longitude","language"],
        "casts": {"date_of_birth":"date","latitude":"decimal:7","longitude":"decimal:7"},
        "rels": [('belongsTo','User','user')],
    },
    "Address": {
        "fillable": ["user_id","label","line1","line2","city","state","country_code","postal_code","latitude","longitude","is_default"],
        "casts": {"is_default":"boolean","latitude":"decimal:7","longitude":"decimal:7"},
        "rels": [('belongsTo','User','user')],
    },
    "Device": {
        "fillable": ["user_id","device_uuid","platform","os_version","app_version","ip_range","risk_score","reputation","first_seen_at","last_seen_at"],
        "casts": {"first_seen_at":"datetime","last_seen_at":"datetime"},
        "rels": [('belongsTo','User','user')],
    },
    "KycVerification": {
        "fillable": ["user_id","level","document_type","document_number","country_code","provider","provider_ref","status","failure_reason","verified_attributes","reviewed_by","reviewed_at","admin_notes"],
        "casts": {"verified_attributes":"array","reviewed_at":"datetime"},
        "rels": [('belongsTo','User','user'),('belongsTo','User','reviewer','reviewed_by')],
    },
    "Role": {
        "fillable": ["name","slug","description"],
        "rels": [('belongsToMany','Permission','permissions','role_permission'),('belongsToMany','User','users','admin_user_role')],
    },
    "Permission": {
        "fillable": ["name","slug","module","description"],
        "rels": [('belongsToMany','Role','roles','role_permission')],
    },
    "SkillCategory": {
        "fillable": ["name","slug","parent_id","icon","is_active","sort_order"],
        "casts": {"is_active":"boolean"},
        "rels": [('belongsTo','SkillCategory','parent','parent_id'),('hasMany','SkillCategory','children','parent_id'),('hasMany','Skill','skills','category_id')],
    },
    "Skill": {
        "fillable": ["category_id","name","slug","description","default_radius_km","is_remote_capable","is_active"],
        "casts": {"is_remote_capable":"boolean","is_active":"boolean"},
        "rels": [('belongsTo','SkillCategory','category','category_id')],
    },
    "Professional": {
        "fillable": ["user_id","jobnixy_id","display_name","headline","about","years_experience","career_level","availability","rating_avg","jobs_completed","jobs_cancelled","completion_rate","skill_verified","offers_warranty","latitude","longitude","service_city","service_state","agent_id","has_smartphone","status"],
        "casts": {"skill_verified":"boolean","offers_warranty":"boolean","has_smartphone":"boolean","rating_avg":"decimal:2","completion_rate":"decimal:2","latitude":"decimal:7","longitude":"decimal:7"},
        "rels": [('belongsTo','User','user'),('belongsTo','User','agent','agent_id'),('belongsToMany','Skill','skills','professional_skills'),('hasMany','Certification','certifications'),('hasMany','PortfolioItem','portfolioItems')],
    },
    "Certification": {
        "fillable": ["professional_id","title","issuer","certificate_number","issued_on","expires_on","status","file_path"],
        "casts": {"issued_on":"date","expires_on":"date"},
        "rels": [('belongsTo','Professional','professional')],
    },
    "PortfolioItem": {
        "fillable": ["professional_id","work_job_id","title","description","image_path","customer_authorized","is_public"],
        "casts": {"customer_authorized":"boolean","is_public":"boolean"},
        "rels": [('belongsTo','Professional','professional'),('belongsTo','WorkJob','workJob','work_job_id')],
    },
    "WorkJob": {
        "table": "work_jobs",
        "fillable": ["reference","employer_id","skill_id","category_id","title","description","hire_mode","work_mode","urgency","status","budget_min","budget_max","estimated_cost_min","estimated_cost_max","currency","preferred_radius_km","max_radius_km","min_kyc_level","min_trust_score","min_experience_years","required_at","estimated_duration_minutes","assigned_professional_id","assigned_agent_id","metadata"],
        "casts": {"required_at":"datetime","metadata":"array","budget_min":"decimal:2","budget_max":"decimal:2","estimated_cost_min":"decimal:2","estimated_cost_max":"decimal:2","min_trust_score":"decimal:2"},
        "rels": [
            ('belongsTo','User','employer','employer_id'),
            ('belongsTo','Skill','skill'),
            ('belongsTo','SkillCategory','category','category_id'),
            ('belongsTo','Professional','assignedProfessional','assigned_professional_id'),
            ('hasOne','JobLocation','location'),
            ('hasMany','JobCandidate','candidates'),
            ('hasMany','JobBid','bids'),
            ('hasOne','Contract','contract'),
            ('hasOne','JobSession','session'),
            ('hasMany','EscrowAccount','escrows'),
            ('hasMany','Dispute','disputes'),
        ],
    },
    "JobLocation": {
        "fillable": ["work_job_id","label","city","state","approx_area","latitude","longitude","exact_revealed"],
        "casts": {"exact_revealed":"boolean","latitude":"decimal:7","longitude":"decimal:7"},
        "rels": [('belongsTo','WorkJob','workJob','work_job_id')],
    },
    "JobCandidate": {
        "fillable": ["work_job_id","professional_id","distance_km","match_score","wave","status","notified_at","responded_at"],
        "casts": {"distance_km":"decimal:2","match_score":"decimal:2","notified_at":"datetime","responded_at":"datetime"},
        "rels": [('belongsTo','WorkJob','workJob','work_job_id'),('belongsTo','Professional','professional')],
    },
    "JobBid": {
        "fillable": ["work_job_id","professional_id","amount","currency","timeframe_days","proposal","status"],
        "casts": {"amount":"decimal:2"},
        "rels": [('belongsTo','WorkJob','workJob','work_job_id'),('belongsTo','Professional','professional')],
    },
    "JobOffer": {
        "fillable": ["work_job_id","professional_id","amount","status","expires_at"],
        "casts": {"amount":"decimal:2","expires_at":"datetime"},
        "rels": [('belongsTo','WorkJob','workJob','work_job_id'),('belongsTo','Professional','professional')],
    },
    "Contract": {
        "fillable": ["reference","work_job_id","employer_id","professional_id","labor_amount","materials_amount","transport_amount","total_amount","currency","warranty_days","completion_date","status","employer_approved_at","professional_approved_at","locked_at"],
        "casts": {"labor_amount":"decimal:2","materials_amount":"decimal:2","transport_amount":"decimal:2","total_amount":"decimal:2","completion_date":"date","employer_approved_at":"datetime","professional_approved_at":"datetime","locked_at":"datetime"},
        "rels": [('belongsTo','WorkJob','workJob','work_job_id'),('belongsTo','User','employer','employer_id'),('belongsTo','Professional','professional'),('hasMany','Milestone','milestones')],
    },
    "Milestone": {
        "fillable": ["contract_id","title","description","amount","sort_order","status","released_at"],
        "casts": {"amount":"decimal:2","released_at":"datetime"},
        "rels": [('belongsTo','Contract','contract')],
    },
    "ChangeOrder": {
        "fillable": ["work_job_id","contract_id","requested_by","additional_amount","reason","status","resolved_at"],
        "casts": {"additional_amount":"decimal:2","resolved_at":"datetime"},
        "rels": [('belongsTo','WorkJob','workJob','work_job_id'),('belongsTo','Contract','contract'),('belongsTo','User','requester','requested_by')],
    },
    "JobSession": {
        "fillable": ["work_job_id","professional_id","dispatched_at","arrived_at","started_at","completed_at","gps_verified","qr_verified","arrival_latitude","arrival_longitude","safety_status"],
        "casts": {"dispatched_at":"datetime","arrived_at":"datetime","started_at":"datetime","completed_at":"datetime","gps_verified":"boolean","qr_verified":"boolean"},
        "rels": [('belongsTo','WorkJob','workJob','work_job_id'),('belongsTo','Professional','professional')],
    },
    "JobEvidence": {
        "table": "job_evidence",
        "fillable": ["work_job_id","uploaded_by","type","file_path","mime_type","notes"],
        "rels": [('belongsTo','WorkJob','workJob','work_job_id'),('belongsTo','User','uploader','uploaded_by')],
    },
    "Agent": {
        "fillable": ["user_id","organization_name","description","service_city","service_state","default_commission_percent","rating_avg","workers_count","status"],
        "casts": {"default_commission_percent":"decimal:2","rating_avg":"decimal:2"},
        "rels": [('belongsTo','User','user'),('hasMany','AgentWorker','workers'),('belongsToMany','Professional','professionals','agent_workers')],
    },
    "AgentWorker": {
        "fillable": ["agent_id","professional_id","worker_percent","agent_percent","platform_percent","status","joined_at","left_at"],
        "casts": {"worker_percent":"decimal:2","agent_percent":"decimal:2","platform_percent":"decimal:2","joined_at":"datetime","left_at":"datetime"},
        "rels": [('belongsTo','Agent','agent'),('belongsTo','Professional','professional')],
    },
    "Agency": {
        "fillable": ["owner_user_id","company_name","cac_number","tax_id","status"],
        "rels": [('belongsTo','User','owner','owner_user_id')],
    },
    "Wallet": {
        "fillable": ["user_id","type","currency","available_balance","pending_balance","ledger_balance","status"],
        "casts": {"available_balance":"decimal:2","pending_balance":"decimal:2","ledger_balance":"decimal:2"},
        "rels": [('belongsTo','User','user'),('hasMany','WalletTransaction','transactions')],
    },
    "WalletTransaction": {
        "fillable": ["wallet_id","reference","type","category","amount","balance_after","currency","status","related_type","related_id","description","idempotency_key"],
        "casts": {"amount":"decimal:2","balance_after":"decimal:2"},
        "rels": [('belongsTo','Wallet','wallet')],
    },
    "EscrowAccount": {
        "fillable": ["reference","work_job_id","contract_id","milestone_id","payer_user_id","amount","released_amount","currency","status","payment_provider","provider_ref","funded_at","released_at"],
        "casts": {"amount":"decimal:2","released_amount":"decimal:2","funded_at":"datetime","released_at":"datetime"},
        "rels": [('belongsTo','WorkJob','workJob','work_job_id'),('belongsTo','Contract','contract'),('belongsTo','User','payer','payer_user_id')],
    },
    "Payment": {
        "fillable": ["reference","user_id","work_job_id","amount","currency","method","provider","provider_ref","direction","status","idempotency_key","meta"],
        "casts": {"amount":"decimal:2","meta":"array"},
        "rels": [('belongsTo','User','user'),('belongsTo','WorkJob','workJob','work_job_id')],
    },
    "Payout": {
        "fillable": ["reference","user_id","wallet_id","amount","currency","bank_name","account_number","account_name","status","failure_reason","processed_at"],
        "casts": {"amount":"decimal:2","processed_at":"datetime"},
        "rels": [('belongsTo','User','user'),('belongsTo','Wallet','wallet')],
    },
    "Commission": {
        "fillable": ["work_job_id","escrow_account_id","gross_amount","worker_amount","agent_amount","platform_amount","worker_percent","agent_percent","platform_percent","status","distributed_at"],
        "casts": {"gross_amount":"decimal:2","worker_amount":"decimal:2","agent_amount":"decimal:2","platform_amount":"decimal:2","worker_percent":"decimal:2","agent_percent":"decimal:2","platform_percent":"decimal:2","distributed_at":"datetime"},
        "rels": [('belongsTo','WorkJob','workJob','work_job_id'),('belongsTo','EscrowAccount','escrow','escrow_account_id')],
    },
    "Review": {
        "fillable": ["work_job_id","reviewer_id","reviewee_id","overall","quality","professionalism","punctuality","communication","pricing_fairness","cleanliness","reliability","comment","is_visible"],
        "casts": {"overall":"decimal:2","quality":"decimal:2","professionalism":"decimal:2","punctuality":"decimal:2","communication":"decimal:2","pricing_fairness":"decimal:2","cleanliness":"decimal:2","reliability":"decimal:2","is_visible":"boolean"},
        "rels": [('belongsTo','WorkJob','workJob','work_job_id'),('belongsTo','User','reviewer','reviewer_id'),('belongsTo','User','reviewee','reviewee_id')],
    },
    "TrustScore": {
        "fillable": ["user_id","score","components","calculated_at"],
        "casts": {"score":"decimal:2","components":"array","calculated_at":"datetime"},
        "rels": [('belongsTo','User','user')],
    },
    "Dispute": {
        "fillable": ["reference","work_job_id","opened_by","against_user_id","reason","description","status","resolution","resolution_notes","assigned_officer_id","resolved_at"],
        "casts": {"resolved_at":"datetime"},
        "rels": [('belongsTo','WorkJob','workJob','work_job_id'),('belongsTo','User','opener','opened_by'),('belongsTo','User','against','against_user_id'),('belongsTo','User','officer','assigned_officer_id'),('hasMany','DisputeEvidence','evidences')],
    },
    "DisputeEvidence": {
        "table": "dispute_evidences",
        "fillable": ["dispute_id","uploaded_by","file_path","notes"],
        "rels": [('belongsTo','Dispute','dispute'),('belongsTo','User','uploader','uploaded_by')],
    },
    "RiskEvent": {
        "fillable": ["user_id","event_type","entity_type","entity_id","risk_score","severity","signals","status","reviewed_by","reviewed_at","notes"],
        "casts": {"signals":"array","reviewed_at":"datetime"},
        "rels": [('belongsTo','User','user'),('belongsTo','User','reviewer','reviewed_by')],
    },
    "SecurityEvent": {
        "fillable": ["user_id","event_type","ip_address","user_agent","payload"],
        "casts": {"payload":"array"},
        "rels": [('belongsTo','User','user')],
    },
    "AuditLog": {
        "fillable": ["admin_user_id","action","auditable_type","auditable_id","old_values","new_values","ip_address","user_agent","device","reason"],
        "casts": {"old_values":"array","new_values":"array"},
        "rels": [('belongsTo','User','admin','admin_user_id')],
    },
    "SupportCase": {
        "fillable": ["reference","user_id","subject","body","priority","status","assigned_to"],
        "rels": [('belongsTo','User','user'),('belongsTo','User','assignee','assigned_to')],
    },
    "PlatformSetting": {
        "fillable": ["key","value","group"],
    },
}

def php_array(items):
    return "[" + ", ".join(f"'{i}'" for i in items) + "]"

def gen_model(name, cfg):
    fills = php_array(cfg["fillable"])
    casts_lines = ""
    if cfg.get("casts"):
        pairs = ",\n            ".join(f"'{k}' => '{v}'" for k,v in cfg["casts"].items())
        casts_lines = f"""
    protected function casts(): array
    {{
        return [
            {pairs},
        ];
    }}
"""
    table_line = f"\n    protected $table = '{cfg['table']}';\n" if cfg.get("table") else ""
    rels = ""
    for r in cfg.get("rels", []):
        kind = r[0]
        related = r[1]
        method = r[2]
        fk = r[3] if len(r) > 3 else None
        if kind == 'belongsTo':
            args = f"{related}::class" + (f", '{fk}'" if fk else "")
            rels += f"""
    public function {method}()
    {{
        return $this->belongsTo({args});
    }}
"""
        elif kind == 'hasMany':
            args = f"{related}::class" + (f", '{fk}'" if fk else "")
            rels += f"""
    public function {method}()
    {{
        return $this->hasMany({args});
    }}
"""
        elif kind == 'hasOne':
            args = f"{related}::class" + (f", '{fk}'" if fk else "")
            rels += f"""
    public function {method}()
    {{
        return $this->hasOne({args});
    }}
"""
        elif kind == 'belongsToMany':
            pivot = r[3]
            rels += f"""
    public function {method}()
    {{
        return $this->belongsToMany({related}::class, '{pivot}');
    }}
"""
    return f"""<?php

namespace App\\Models;

use Illuminate\\Database\\Eloquent\\Model;
use Illuminate\\Database\\Eloquent\\Factories\\HasFactory;

class {name} extends Model
{{
    use HasFactory;
{table_line}
    protected $fillable = {fills};
{casts_lines}{rels}}}
"""

models_dir = ROOT / "app/Models"
for name, cfg in MODELS.items():
    (models_dir / f"{name}.php").write_text(gen_model(name, cfg))
    print(f"Model: {name}")

print("Models done:", len(MODELS))
