<?php
/**
 * Partners, capital and monthly profit distribution.
 * Capital/equity movements are deliberately separate from operating expenses.
 */
require_once __DIR__ . '/finance_bootstrap.php';
require_once __DIR__ . '/purchases_lib.php';
require_once __DIR__ . '/billing_account_lib.php';

if (!function_exists('finance_partners_migrate')) {
  function finance_partners_migrate(mysqli $conn): void {
    static $done = [];
    $key = spl_object_id($conn);
    if (!empty($done[$key])) return;
    ensure_purchase_tables($conn);
    ua_ensure_column($conn, 'shop_purchases', 'accounting_class', "VARCHAR(20) NOT NULL DEFAULT 'OPERATING'");
    ua_ensure_column($conn, 'shop_purchases', 'capital_allocation_mode', 'VARCHAR(12) NULL');
    ua_ensure_column($conn, 'shop_purchases', 'capital_partner_id', 'INT NULL');

    $conn->query("CREATE TABLE IF NOT EXISTS finance_partners (
      id INT AUTO_INCREMENT PRIMARY KEY,
      code VARCHAR(50) NULL,
      name VARCHAR(160) NOT NULL,
      ownership_percent DECIMAL(9,6) NOT NULL DEFAULT 0,
      is_active TINYINT(1) NOT NULL DEFAULT 1,
      notes VARCHAR(255) NULL,
      created_by INT NULL,
      created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
      updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
      UNIQUE KEY uq_finance_partner_code(code), KEY idx_finance_partner_active(is_active,name)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
    $conn->query("CREATE TABLE IF NOT EXISTS finance_partner_ledger (
      id BIGINT AUTO_INCREMENT PRIMARY KEY,
      partner_id INT NOT NULL,
      account_kind VARCHAR(12) NOT NULL,
      direction VARCHAR(10) NOT NULL,
      entry_type VARCHAR(32) NOT NULL,
      amount DECIMAL(16,2) NOT NULL,
      reference_type VARCHAR(30) NULL,
      reference_id BIGINT NULL,
      effective_date DATE NOT NULL,
      note VARCHAR(255) NULL,
      status VARCHAR(20) NOT NULL DEFAULT 'POSTED',
      idempotency_key VARCHAR(191) NULL,
      created_by INT NULL,
      created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
      UNIQUE KEY uq_partner_ledger_idempotency(idempotency_key),
      KEY idx_partner_ledger_balance(partner_id,account_kind,status,effective_date),
      KEY idx_partner_ledger_reference(reference_type,reference_id)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
    $conn->query("CREATE TABLE IF NOT EXISTS finance_profit_periods (
      id INT AUTO_INCREMENT PRIMARY KEY,
      month_key CHAR(7) NOT NULL,
      revenue_total DECIMAL(16,2) NOT NULL DEFAULT 0,
      expense_total DECIMAL(16,2) NOT NULL DEFAULT 0,
      operating_purchases_total DECIMAL(16,2) NOT NULL DEFAULT 0,
      net_profit DECIMAL(16,2) NOT NULL DEFAULT 0,
      status VARCHAR(20) NOT NULL DEFAULT 'POSTED',
      snapshot_json LONGTEXT NULL,
      posted_by INT NULL,
      posted_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
      UNIQUE KEY uq_profit_period_month(month_key)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
    $conn->query("CREATE TABLE IF NOT EXISTS finance_profit_allocations (
      id BIGINT AUTO_INCREMENT PRIMARY KEY,
      period_id INT NOT NULL,
      partner_id INT NOT NULL,
      ownership_percent DECIMAL(9,6) NOT NULL,
      allocated_amount DECIMAL(16,2) NOT NULL,
      created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
      UNIQUE KEY uq_profit_partner(period_id,partner_id), KEY idx_profit_alloc_partner(partner_id)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
    $done[$key] = true;
  }
}

if (!function_exists('finance_partners_list')) {
  function finance_partners_list(mysqli $conn, bool $activeOnly = false): array {
    finance_partners_migrate($conn);
    $where = $activeOnly ? 'WHERE p.is_active=1' : '';
    $sql = "SELECT p.*,
      COALESCE(SUM(CASE WHEN l.status='POSTED' AND l.account_kind='CAPITAL' THEN IF(l.direction='CREDIT',l.amount,-l.amount) ELSE 0 END),0) capital_balance,
      COALESCE(SUM(CASE WHEN l.status='POSTED' AND l.account_kind='PROFIT' THEN IF(l.direction='CREDIT',l.amount,-l.amount) ELSE 0 END),0) profit_balance
      FROM finance_partners p LEFT JOIN finance_partner_ledger l ON l.partner_id=p.id {$where}
      GROUP BY p.id ORDER BY p.is_active DESC,p.name";
    $rows=[]; $rs=$conn->query($sql); while($rs && ($row=$rs->fetch_assoc())) $rows[]=$row; return $rows;
  }
}

if (!function_exists('finance_partner_save')) {
  function finance_partner_save(mysqli $conn, array $data, int $actorId=0): array {
    finance_partners_migrate($conn);
    $id=(int)($data['id']??0); $name=trim((string)($data['name']??''));
    $code=substr(trim((string)($data['code']??'')),0,50); $share=round((float)($data['ownership_percent']??0),6);
    $notes=substr(trim((string)($data['notes']??'')),0,255); $active=!empty($data['is_active'])?1:0;
    if($name==='' || $share<0 || $share>100) return ['ok'=>false,'error'=>'اسم الشريك ونسبة ملكية بين 0 و100 مطلوبان.'];
    $othersSql=$id>0?' AND id<>'.$id:''; $rs=$conn->query("SELECT COALESCE(SUM(ownership_percent),0) total FROM finance_partners WHERE is_active=1{$othersSql}");
    $others=(float)(($rs?$rs->fetch_assoc():[])['total']??0);
    if($active && $others+$share>100.000001) return ['ok'=>false,'error'=>'مجموع حصص الشركاء النشطين لا يجوز أن يتجاوز 100%.'];
    if($id>0){
      $stmt=$conn->prepare("UPDATE finance_partners SET code=NULLIF(?,''),name=?,ownership_percent=?,is_active=?,notes=? WHERE id=?");
      $stmt->bind_param('ssdisi',$code,$name,$share,$active,$notes,$id);
    }else{
      $stmt=$conn->prepare("INSERT INTO finance_partners(code,name,ownership_percent,is_active,notes,created_by) VALUES(NULLIF(?,''),?,?,?,?,?)");
      $actor=$actorId>0?$actorId:null; $stmt->bind_param('ssdisi',$code,$name,$share,$active,$notes,$actor);
    }
    $ok=$stmt->execute(); $error=$ok?'':$stmt->error; $newId=$id>0?$id:(int)$stmt->insert_id; $stmt->close();
    return ['ok'=>$ok,'id'=>$newId,'error'=>$error];
  }
}

if (!function_exists('finance_partner_post_ledger')) {
  function finance_partner_post_ledger(mysqli $conn,int $partnerId,string $kind,string $direction,float $amount,array $options=[]): array {
    finance_partners_migrate($conn); $kind=strtoupper($kind); $direction=strtoupper($direction); $amount=round(abs($amount),2);
    if($partnerId<=0 || $amount<=0 || !in_array($kind,['CAPITAL','PROFIT'],true) || !in_array($direction,['CREDIT','DEBIT'],true)) return ['ok'=>false,'error'=>'بيانات قيد الشريك غير صالحة.'];
    $type=substr(strtoupper(trim((string)($options['entry_type']??'ADJUSTMENT'))),0,32); $refType=substr(trim((string)($options['reference_type']??'')),0,30);
    $refId=(int)($options['reference_id']??0); $day=(string)($options['effective_date']??date('Y-m-d')); $note=substr(trim((string)($options['note']??'')),0,255);
    $key=substr(trim((string)($options['idempotency_key']??'')),0,191); $actor=(int)($options['created_by']??0);
    if(!preg_match('/^\d{4}-\d{2}-\d{2}$/',$day)) return ['ok'=>false,'error'=>'تاريخ القيد غير صالح.'];
    $stmt=$conn->prepare("INSERT INTO finance_partner_ledger(partner_id,account_kind,direction,entry_type,amount,reference_type,reference_id,effective_date,note,idempotency_key,created_by) VALUES(?,?,?,?,?,?,NULLIF(?,0),?,?,?,NULLIF(?,0))");
    $stmt->bind_param('isssdsisssi',$partnerId,$kind,$direction,$type,$amount,$refType,$refId,$day,$note,$key,$actor);
    try{$executed=$stmt->execute();}catch(mysqli_sql_exception $e){
      if((int)$e->getCode()===1062){$stmt->close();return ['ok'=>true,'duplicate'=>true];}
      $stmt->close();return ['ok'=>false,'error'=>$e->getMessage()];
    }
    if(!$executed){$error=$stmt->error;$stmt->close();return ['ok'=>false,'error'=>$error];}
    $id=(int)$stmt->insert_id;$stmt->close();return ['ok'=>true,'id'=>$id,'duplicate'=>false];
  }
}

if (!function_exists('finance_capital_allocations')) {
  function finance_capital_allocations(mysqli $conn,float $amount,string $mode,int $partnerId=0): array {
    $partners=finance_partners_list($conn,true); $amount=round($amount,2); if($amount<=0)return [];
    if($mode==='ONE') foreach($partners as $p) if((int)$p['id']===$partnerId)return [['partner_id'=>$partnerId,'amount'=>$amount,'name'=>$p['name']]];
    $total=array_sum(array_map(fn($p)=>(float)$p['ownership_percent'],$partners)); if(!$partners || abs($total-100)>0.0001)return [];
    $out=[];$used=0.0;$last=count($partners)-1;
    foreach($partners as $i=>$p){$part=$i===$last?round($amount-$used,2):round($amount*((float)$p['ownership_percent']/100),2);$used+=$part;$out[]=['partner_id'=>(int)$p['id'],'amount'=>$part,'name'=>$p['name']];}
    return $out;
  }
}

if (!function_exists('finance_record_capital_purchase')) {
  function finance_record_capital_purchase(mysqli $conn,int $purchaseId,float $amountSyp,string $mode,int $partnerId,int $actorId=0,string $day=''): array {
    $alloc=finance_capital_allocations($conn,$amountSyp,$mode,$partnerId); if(!$alloc)return ['ok'=>false,'error'=>'تعذر توزيع رأس المال: اختر شريكًا صالحًا أو أكمل حصص الشركاء إلى 100%.'];
    foreach($alloc as $row){$posted=finance_partner_post_ledger($conn,(int)$row['partner_id'],'CAPITAL','CREDIT',(float)$row['amount'],[
      'entry_type'=>'CAPITAL_PURCHASE','reference_type'=>'PURCHASE','reference_id'=>$purchaseId,'effective_date'=>$day?:date('Y-m-d'),'note'=>'شراء ممول كرأس مال','idempotency_key'=>'capital-purchase-'.$purchaseId.'-'.$row['partner_id'],'created_by'=>$actorId]);
      if(empty($posted['ok']))return $posted;
    } return ['ok'=>true,'allocations'=>$alloc];
  }
}

if (!function_exists('finance_profit_snapshot')) {
  function finance_profit_snapshot(mysqli $conn,string $month): array {
    finance_partners_migrate($conn); if(!preg_match('/^\d{4}-\d{2}$/',$month))$month=date('Y-m');
    $start=$month.'-01';$end=date('Y-m-d',strtotime($start.' +1 month'));
    $escStart=$conn->real_escape_string($start);$escEnd=$conn->real_escape_string($end);
    $payStatus=finance_column_exists($conn,'payments','payment_status')?"AND COALESCE(payment_status,'POSTED')='POSTED'":'';
    $payDate=finance_column_exists($conn,'payments','paid_at')?'COALESCE(paid_at,created_at)':'created_at';
    $rs=$conn->query("SELECT COALESCE(SUM(amount),0) total FROM payments WHERE {$payDate}>='{$escStart}' AND {$payDate}<'{$escEnd}' {$payStatus}");$revenue=(float)(($rs?$rs->fetch_assoc():[])['total']??0);
    $expense=0.0;if(finance_table_exists($conn,'expenses')){$status=finance_column_exists($conn,'expenses','expense_status')?"AND COALESCE(expense_status,'POSTED')='POSTED'":'';$dateCol=finance_column_exists($conn,'expenses','spent_at')?'spent_at':'expense_date';$rs=$conn->query("SELECT COALESCE(SUM(amount),0) total FROM expenses WHERE {$dateCol}>='{$escStart}' AND {$dateCol}<'{$escEnd}' {$status}");$expense=(float)(($rs?$rs->fetch_assoc():[])['total']??0);}
    $rs=$conn->query("SELECT COALESCE(SUM(CASE WHEN currency='USD' THEN total*COALESCE(NULLIF(usd_rate,0),0) ELSE total END),0) total,COALESCE(SUM(CASE WHEN currency='USD' AND COALESCE(usd_rate,0)<=0 THEN total ELSE 0 END),0) missing_usd FROM shop_purchases WHERE created_at>='{$escStart}' AND created_at<'{$escEnd}' AND accounting_class='OPERATING'");
    $pr=$rs?$rs->fetch_assoc():[];$purchases=(float)($pr['total']??0);$missing=(float)($pr['missing_usd']??0);$net=round($revenue-$expense-$purchases,2);
    return ['month'=>$month,'revenue'=>round($revenue,2),'expenses'=>round($expense,2),'operating_purchases'=>round($purchases,2),'missing_usd'=>$missing,'net_profit'=>$net,'start'=>$start,'end'=>$end];
  }
}

if (!function_exists('finance_profit_post')) {
  function finance_profit_post(mysqli $conn,string $month,int $actorId=0): array {
    finance_partners_migrate($conn);$snap=finance_profit_snapshot($conn,$month);$partners=finance_partners_list($conn,true);
    $share=array_sum(array_map(fn($p)=>(float)$p['ownership_percent'],$partners));
    if(!$partners || abs($share-100)>0.0001)return ['ok'=>false,'error'=>'يجب أن يساوي مجموع حصص الشركاء النشطين 100% قبل التوزيع.'];
    if((float)$snap['missing_usd']>0)return ['ok'=>false,'error'=>'توجد مشتريات بالدولار دون سعر صرف؛ لا يمكن تثبيت ربح غير دقيق.'];
    if((float)$snap['net_profit']<=0)return ['ok'=>false,'error'=>'لا يوجد ربح موجب قابل للتوزيع في هذا الشهر.'];
    [$year,$mon]=array_map('intval',explode('-',$month)); if(!finance_is_month_closed($conn,$year,$mon))return ['ok'=>false,'error'=>'يجب إغلاق الشهر محاسبيًا قبل تثبيت توزيع الأرباح.'];
    try{$conn->begin_transaction();$json=json_encode(['snapshot'=>$snap,'partners'=>array_map(fn($p)=>['id'=>(int)$p['id'],'name'=>$p['name'],'share'=>(float)$p['ownership_percent']],$partners)],JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES);
      $stmt=$conn->prepare("INSERT INTO finance_profit_periods(month_key,revenue_total,expense_total,operating_purchases_total,net_profit,snapshot_json,posted_by) VALUES(?,?,?,?,?,?,NULLIF(?,0))");
      $stmt->bind_param('sddddsi',$month,$snap['revenue'],$snap['expenses'],$snap['operating_purchases'],$snap['net_profit'],$json,$actorId);if(!$stmt->execute())throw new RuntimeException($stmt->errno===1062?'تم تثبيت توزيع هذا الشهر مسبقًا.':$stmt->error);$periodId=(int)$stmt->insert_id;$stmt->close();
      $alloc=finance_capital_allocations($conn,(float)$snap['net_profit'],'ALL',0);foreach($alloc as $row){$partnerId=(int)$row['partner_id'];$partner=null;foreach($partners as $p)if((int)$p['id']===$partnerId){$partner=$p;break;}$pct=(float)($partner['ownership_percent']??0);$amount=(float)$row['amount'];
        $stmt=$conn->prepare("INSERT INTO finance_profit_allocations(period_id,partner_id,ownership_percent,allocated_amount) VALUES(?,?,?,?)");$stmt->bind_param('iidd',$periodId,$partnerId,$pct,$amount);if(!$stmt->execute())throw new RuntimeException($stmt->error);$stmt->close();
        $posted=finance_partner_post_ledger($conn,$partnerId,'PROFIT','CREDIT',$amount,['entry_type'=>'PROFIT_ALLOCATION','reference_type'=>'PROFIT_PERIOD','reference_id'=>$periodId,'effective_date'=>$snap['end'],'note'=>'توزيع أرباح '.$month,'idempotency_key'=>'profit-'.$month.'-'.$partnerId,'created_by'=>$actorId]);if(empty($posted['ok']))throw new RuntimeException((string)($posted['error']??'تعذر ترحيل حصة الشريك.'));
      }$conn->commit();return ['ok'=>true,'period_id'=>$periodId,'snapshot'=>$snap,'allocations'=>$alloc];
    }catch(Throwable $e){@$conn->rollback();return ['ok'=>false,'error'=>$e->getMessage()];}
  }
}

if (!function_exists('finance_partner_cashboxes')) {
  function finance_partner_cashboxes(mysqli $conn): array {
    billing_accounts_migrate($conn);$rows=billing_collection_balances($conn);
    if(!$rows)$rows[]=['account_key'=>'MAIN','account_type'=>'main','account_ref'=>'main','account_name'=>'النسخة الرئيسية','balance'=>0];
    return $rows;
  }
}

if (!function_exists('finance_partner_profit_payout')) {
  function finance_partner_profit_payout(mysqli $conn,int $partnerId,float $amount,string $accountKey,string $day,int $actorId=0,string $note=''): array {
    finance_partners_migrate($conn);$amount=round(abs($amount),2);$accountKey=trim($accountKey);
    if($partnerId<=0||$amount<=0||$accountKey===''||!preg_match('/^\d{4}-\d{2}-\d{2}$/',$day))return ['ok'=>false,'error'=>'بيانات صرف حصة الأرباح غير صالحة.'];
    if(finance_is_month_closed($conn,(int)date('Y',strtotime($day)),(int)date('n',strtotime($day))))return ['ok'=>false,'error'=>'لا يمكن الصرف بتاريخ يقع ضمن شهر مغلق محاسبيًا.'];
    try{$conn->begin_transaction();
      $rs=$conn->query("SELECT id FROM finance_partner_ledger WHERE partner_id={$partnerId} AND account_kind='PROFIT' AND status='POSTED' FOR UPDATE");
      $rs=$conn->query("SELECT COALESCE(SUM(IF(direction='CREDIT',amount,-amount)),0) balance FROM finance_partner_ledger WHERE partner_id={$partnerId} AND account_kind='PROFIT' AND status='POSTED'");$profit=(float)(($rs?$rs->fetch_assoc():[])['balance']??0);
      if($profit+0.00001<$amount)throw new RuntimeException('رصيد أرباح الشريك غير كافٍ.');
      $keyEsc=$conn->real_escape_string($accountKey);$rs=$conn->query("SELECT * FROM collection_accounts WHERE account_key='{$keyEsc}' LIMIT 1 FOR UPDATE");$source=$rs&&$rs->num_rows?$rs->fetch_assoc():null;if(!$source)throw new RuntimeException('الصندوق المحدد غير موجود.');
      if((float)$source['balance']+0.00001<$amount)throw new RuntimeException('رصيد الصندوق المحدد لا يغطي صرف الحصة.');
      if($accountKey!=='MAIN'){$rs=$conn->query("SELECT balance FROM collection_accounts WHERE account_key='MAIN' LIMIT 1 FOR UPDATE");$main=(float)(($rs?$rs->fetch_assoc():[])['balance']??0);if($main+0.00001<$amount)throw new RuntimeException('رصيد الصندوق الرئيسي لا يغطي صرف الحصة.');}
      $token='partner-payout-'.bin2hex(random_bytes(12));$entry=finance_partner_post_ledger($conn,$partnerId,'PROFIT','DEBIT',$amount,['entry_type'=>'PROFIT_PAYOUT','reference_type'=>'CASHBOX','effective_date'=>$day,'note'=>$note?:'صرف حصة أرباح','idempotency_key'=>$token,'created_by'=>$actorId]);if(empty($entry['ok']))throw new RuntimeException((string)($entry['error']??'تعذر ترحيل حصة الشريك.'));
      $common=['entry_type'=>'PARTNER_PAYOUT','channel'=>'desktop','note'=>($note?:'صرف أرباح شريك').' #'.$partnerId];
      $mainPost=billing_collection_post($conn,'MAIN','main','main','النسخة الرئيسية','DEBIT',$amount,$common+['idempotency_key'=>'COLLECTION:MAIN:'.$token]);if(empty($mainPost['ok']))throw new RuntimeException((string)($mainPost['error']??'تعذر الخصم من الصندوق الرئيسي.'));
      $selected=$mainPost;if($accountKey!=='MAIN'){$selected=billing_collection_post($conn,$accountKey,(string)$source['account_type'],(string)$source['account_ref'],(string)$source['account_name'],'DEBIT',$amount,$common+['idempotency_key'=>'COLLECTION:SOURCE:'.$token]);if(empty($selected['ok']))throw new RuntimeException((string)($selected['error']??'تعذر الخصم من الصندوق المحدد.'));}
      $conn->query("UPDATE finance_partner_ledger SET reference_id=".(int)($selected['id']??0)." WHERE id=".(int)($entry['id']??0));$conn->commit();return ['ok'=>true,'profit_balance'=>round($profit-$amount,2),'cashbox_balance'=>(float)($selected['balance']??0)];
    }catch(Throwable $e){@$conn->rollback();return ['ok'=>false,'error'=>$e->getMessage()];}
  }
}
