<?php

namespace Espo\Custom\Services;

use Espo\Core\Exceptions\Forbidden;
use Espo\Core\Exceptions\NotFound;
use Espo\Core\Exceptions\BadRequest;
use Espo\Tools\Stream\Service as StreamService;
use Espo\Entities\Note;

class CVmhRumungsklage extends \Espo\Services\Record
{
    /**
     * Create Räumungsklage from any source entity (Mietverhältnis or Kündigung)
     * 
     * @param string $sourceEntityType Entity type (CVmhMietverhltnis or CKuendigung)
     * @param string $sourceEntityId Entity ID
     * @return array
     * @throws NotFound
     * @throws Forbidden
     * @throws BadRequest
     */
    public function createFromSource(string $sourceEntityType, string $sourceEntityId): array
    {
        // 1. Load source entity
        $sourceEntity = $this->entityManager->getEntity($sourceEntityType, $sourceEntityId);
        if (!$sourceEntity) {
            throw new NotFound("$sourceEntityType not found");
        }
        
        // 2. ACL Checks
        if (!$this->acl->check($sourceEntity, 'read')) {
            throw new Forbidden("No read access to $sourceEntityType");
        }
        
        if (!$this->acl->checkScope('CVmhRumungsklage', 'create')) {
            throw new Forbidden('No create access to Räumungsklage');
        }
        
        // 3. Start Transaction
        $this->entityManager->getTransactionManager()->start();
        
        try {
            // 4. Collect all related Mietverhältnisse and Kündigungen
            [$alleMietverhaeltnisse, $alleKuendigungen] = $this->collectRelatedEntities(
                $sourceEntityType, 
                $sourceEntity
            );
            
            // 5. Create Räumungsklage from collected entities
            $result = $this->createFromCollectedEntities($alleMietverhaeltnisse, $alleKuendigungen);
            
            // 6. Log to stream
            $this->logToStream($sourceEntity, $sourceEntityType, $result['id']);
            
            // 7. Commit transaction
            $this->entityManager->getTransactionManager()->commit();
            
            return $result;
        } catch (\Exception $e) {
            // Rollback on any error
            $this->entityManager->getTransactionManager()->rollback();
            throw $e;
        }
    }
    
    /**
     * Collect all related Mietverhältnisse and Kündigungen from source entity
     * 
     * @param string $sourceEntityType
     * @param object $sourceEntity
     * @return array [alleMietverhaeltnisse, alleKuendigungen]
     */
    private function collectRelatedEntities(string $sourceEntityType, $sourceEntity): array
    {
        $alleMietverhaeltnisse = [];
        $alleKuendigungen = [];
        
        if ($sourceEntityType === 'CVmhMietverhltnis') {
            // Start from Mietverhältnis
            $alleMietverhaeltnisse = [$sourceEntity];
            
            // Collect all Kündigungen from this Mietverhältnis
            $kuendigungen = $this->entityManager
                ->getRepository('CVmhMietverhltnis')
                ->getRelation($sourceEntity, 'kndigungen')
                ->find();
            
            foreach ($kuendigungen as $kuendigung) {
                $alleKuendigungen[] = $kuendigung;
            }
            
        } elseif ($sourceEntityType === 'CKuendigung') {
            // Start from Kündigung
            $alleKuendigungen = [$sourceEntity];
            $alleKuendigungIds = [$sourceEntity->getId()];
            
            // Collect all Mietverhältnisse from this Kündigung
            $mietverhaeltnisse = $this->entityManager
                ->getRepository('CKuendigung')
                ->getRelation($sourceEntity, 'vmhMietverhltnises')
                ->find();
            
            foreach ($mietverhaeltnisse as $mv) {
                $alleMietverhaeltnisse[] = $mv;
            }
            
            // Collect any other Kündigungen from these Mietverhältnisse
            foreach ($alleMietverhaeltnisse as $mv) {
                $kuendigungen = $this->entityManager
                    ->getRepository('CVmhMietverhltnis')
                    ->getRelation($mv, 'kndigungen')
                    ->find();
                
                foreach ($kuendigungen as $k) {
                    if (!in_array($k->getId(), $alleKuendigungIds)) {
                        $alleKuendigungen[] = $k;
                        $alleKuendigungIds[] = $k->getId();
                    }
                }
            }
        } else {
            throw new BadRequest("Unsupported source entity type: $sourceEntityType");
        }
        
        return [$alleMietverhaeltnisse, $alleKuendigungen];
    }
    
    /**
     * Create Räumungsklage from collected Mietverhältnisse and Kündigungen
     * 
     * @param array $alleMietverhaeltnisse
     * @param array $alleKuendigungen
     * @return array
     */
    private function createFromCollectedEntities(array $alleMietverhaeltnisse, array $alleKuendigungen): array
    {
        // 1. Get Advoware fields from Kündigungen (first with values)
        $advowareAktenzeichen = null;
        $aktennr = null;
        
        foreach ($alleKuendigungen as $kuendigung) {
            if (!$advowareAktenzeichen && $kuendigung->get('advowareAktenzeichen')) {
                $advowareAktenzeichen = $kuendigung->get('advowareAktenzeichen');
            }
            if (!$aktennr && $kuendigung->get('aktennr')) {
                $aktennr = $kuendigung->get('aktennr');
            }
            if ($advowareAktenzeichen && $aktennr) {
                break; // Found both, no need to continue
            }
        }
        
        // 2. Prepare data for new Räumungsklage
        $data = new \stdClass();
        
        // Use name from first Mietverhältnis
        $data->name = 'Räumungsklage - ' . $alleMietverhaeltnisse[0]->get('name');
        
        // Copy Advoware fields if found
        if ($advowareAktenzeichen) {
            $data->advowareAktenzeichen = $advowareAktenzeichen;
        }
        if ($aktennr) {
            $data->aktennr = $aktennr;
        }
        
        // Copy assignedUser and teams from first Mietverhältnis
        if ($alleMietverhaeltnisse[0]->get('assignedUserId')) {
            $data->assignedUserId = $alleMietverhaeltnisse[0]->get('assignedUserId');
        }
        
        $teamsIds = $alleMietverhaeltnisse[0]->getLinkMultipleIdList('teams');
        if (!empty($teamsIds)) {
            $data->teamsIds = $teamsIds;
        }
        
        // 3. Create Räumungsklage entity
        $raeumungsklage = $this->entityManager->createEntity('CVmhRumungsklage', (array)$data);
        
        if (!$raeumungsklage) {
            throw new \RuntimeException('Failed to create Räumungsklage');
        }
        
        $raeumungsklagenRepo = $this->entityManager->getRepository('CVmhRumungsklage');
        
        // 4. Link all Mietverhältnisse to Räumungsklage
        foreach ($alleMietverhaeltnisse as $mv) {
            $raeumungsklagenRepo
                ->getRelation($raeumungsklage, 'vmhMietverhltnises')
                ->relate($mv);
        }
        
        // 5. Collect all Mietobjekte from all Mietverhältnisse (for document collection only)
        $alleMietobjekte = [];
        foreach ($alleMietverhaeltnisse as $mv) {
            $mietobjekt = $this->entityManager
                ->getRepository('CVmhMietverhltnis')
                ->getRelation($mv, 'vmhMietobjekt')
                ->findOne();
            
            if ($mietobjekt && !in_array($mietobjekt->getId(), array_map(fn($m) => $m->getId(), $alleMietobjekte))) {
                $alleMietobjekte[] = $mietobjekt;
            }
        }
        
        // Note: Mietobjekte werden NICHT mehr direkt zur Räumungsklage gelinkt
        // Die Verbindung erfolgt indirekt über Mietverhältnisse
        
        // 6. Collect all Beteiligte from all Mietverhältnisse
        $alleVermieter = [];
        $alleMieter = [];
        $alleSonstigeBewohner = [];
        
        foreach ($alleMietverhaeltnisse as $mv) {
            // Vermieter
            $vermieterBeteiligte = $this->entityManager
                ->getRepository('CVmhMietverhltnis')
                ->getRelation($mv, 'vmhbeteiligtevermieter')
                ->find();
            
            foreach ($vermieterBeteiligte as $vermieter) {
                if (!in_array($vermieter->getId(), array_map(fn($v) => $v->getId(), $alleVermieter))) {
                    $alleVermieter[] = $vermieter;
                }
            }
            
            // Mieter
            $mieterBeteiligte = $this->entityManager
                ->getRepository('CVmhMietverhltnis')
                ->getRelation($mv, 'vmhbeteiligtemieter')
                ->find();
            
            foreach ($mieterBeteiligte as $mieter) {
                if (!in_array($mieter->getId(), array_map(fn($m) => $m->getId(), $alleMieter))) {
                    $alleMieter[] = $mieter;
                }
            }
            
            // Sonstige Bewohner
            $sonstigeBewohner = $this->entityManager
                ->getRepository('CVmhMietverhltnis')
                ->getRelation($mv, 'sonstigebesitzervmhmietverhltnis')
                ->find();
            
            foreach ($sonstigeBewohner as $bewohner) {
                if (!in_array($bewohner->getId(), array_map(fn($b) => $b->getId(), $alleSonstigeBewohner))) {
                    $alleSonstigeBewohner[] = $bewohner;
                }
            }
        }
        
        // Link Vermieter as Kläger
        foreach ($alleVermieter as $vermieter) {
            $raeumungsklagenRepo
                ->getRelation($raeumungsklage, 'klaeger')
                ->relate($vermieter);
        }
        
        // Link Mieter and Sonstige as Beklagte
        foreach (array_merge($alleMieter, $alleSonstigeBewohner) as $beklagter) {
            $raeumungsklagenRepo
                ->getRelation($raeumungsklage, 'beklagte')
                ->relate($beklagter);
        }
        
        // 7. Collect all documents from Mietverhältnisse, Kündigungen, Mietobjekte and Beteiligte
        $alleLinkedDokumente = [];
        
        // 7a. Dokumente from all Mietverhältnisse
        foreach ($alleMietverhaeltnisse as $mv) {
            $dokumenteMV = $this->entityManager
                ->getRepository('CVmhMietverhltnis')
                ->getRelation($mv, 'dokumentesvmhMietverhltnisse')
                ->find();
            
            foreach ($dokumenteMV as $dokument) {
                if (!in_array($dokument->getId(), $alleLinkedDokumente)) {
                    $alleLinkedDokumente[] = $dokument->getId();
                    $raeumungsklagenRepo
                        ->getRelation($raeumungsklage, 'dokumentesvmhraumungsklage')
                        ->relate($dokument);
                }
            }
        }
        
        // 7b. Dokumente from all Kündigungen
        foreach ($alleKuendigungen as $kuendigung) {
            $dokumenteKuendigung = $this->entityManager
                ->getRepository('CKuendigung')
                ->getRelation($kuendigung, 'dokumenteskuendigung')
                ->find();
            
            foreach ($dokumenteKuendigung as $dokument) {
                if (!in_array($dokument->getId(), $alleLinkedDokumente)) {
                    $alleLinkedDokumente[] = $dokument->getId();
                    $raeumungsklagenRepo
                        ->getRelation($raeumungsklage, 'dokumentesvmhraumungsklage')
                        ->relate($dokument);
                }
            }
        }
        
        // 7c. Dokumente from all Mietobjekte
        foreach ($alleMietobjekte as $mietobjekt) {
            $dokumenteMO = $this->entityManager
                ->getRepository('CMietobjekt')
                ->getRelation($mietobjekt, 'dokumentesMietobjekt')
                ->find();
            
            foreach ($dokumenteMO as $dokument) {
                if (!in_array($dokument->getId(), $alleLinkedDokumente)) {
                    $alleLinkedDokumente[] = $dokument->getId();
                    $raeumungsklagenRepo
                        ->getRelation($raeumungsklage, 'dokumentesvmhraumungsklage')
                        ->relate($dokument);
                }
            }
        }
        
        // 7d. Dokumente from all Beteiligte
        $alleBeteiligte = array_merge($alleVermieter, $alleMieter, $alleSonstigeBewohner);
        foreach ($alleBeteiligte as $beteiligter) {
            $dokumenteBet = $this->entityManager
                ->getRepository('CBeteiligte')
                ->getRelation($beteiligter, 'dokumentesBeteiligte')
                ->find();
            
            foreach ($dokumenteBet as $dokument) {
                if (!in_array($dokument->getId(), $alleLinkedDokumente)) {
                    $alleLinkedDokumente[] = $dokument->getId();
                    $raeumungsklagenRepo
                        ->getRelation($raeumungsklage, 'dokumentesvmhraumungsklage')
                        ->relate($dokument);
                }
            }
        }
        
        // 8. Copy portal contacts from all Mietverhältnisse
        $alleLinkedContacts = [];
        foreach ($alleMietverhaeltnisse as $mv) {
            $portalContacts = $this->entityManager
                ->getRepository('CVmhMietverhltnis')
                ->getRelation($mv, 'contactsMietverhltnis')
                ->find();
            
            foreach ($portalContacts as $contact) {
                if (!in_array($contact->getId(), $alleLinkedContacts)) {
                    $alleLinkedContacts[] = $contact->getId();
                    $raeumungsklagenRepo
                        ->getRelation($raeumungsklage, 'contactsRumungsklage')
                        ->relate($contact);
                }
            }
        }
        
        return [
            'id' => $raeumungsklage->getId(),
            'name' => $raeumungsklage->get('name')
        ];
    }
    
    /**
     * Log Räumungsklage creation to source entity stream
     * 
     * @param object $sourceEntity Source entity (Mietverhältnis or Kündigung)
     * @param string $sourceEntityType Entity type
     * @param string $raeumungsklagenId Created Räumungsklage ID
     */
    private function logToStream($sourceEntity, string $sourceEntityType, string $raeumungsklagenId): void
    {
        // Create stream service
        $streamService = $this->injectableFactory->create(StreamService::class);
        
        // Create Note entity manually for custom message
        $note = $this->entityManager->getEntity('Note');
        $note->set([
            'type' => Note::TYPE_CREATE_RELATED,
            'parentType' => $sourceEntityType,
            'parentId' => $sourceEntity->getId(),
            'relatedType' => 'CVmhRumungsklage',
            'relatedId' => $raeumungsklagenId,
            'data' => [
                'action' => 'initiateEviction',
                'actionLabel' => 'Räumungsklage einleiten'
            ]
        ]);
        
        $this->entityManager->saveEntity($note);
    }
}

