<?php
namespace Controllers;

class FinancesController
{
  private static function repo()
  {
    return new \Domain\Finances\FinanceRepository();
  }

  private static function importRepo()
  {
    return new \Domain\Finances\FakturowniaImportRepository();
  }

  private static function requireAuth()
  {
    global $user;

    if ( !$user )
    {
      \controls\Users::login_form();
      return false;
    }
    return true;
  }

  private static function resolveDates()
  {
    $dates = \S::get_session( 'finance-dates' );
    $dates = explode( '_-_', $dates );

    $date_from = $dates[0] ? $dates[0] : date( 'Y-m-01' );
    $date_to = $dates[1] ? $dates[1] : date( 'Y-m-t' );

    return [ $date_from, $date_to ];
  }

  private static function resolveGroup( $repo )
  {
    if ( \S::get( 'group-id' ) )
      \S::set_session( 'finance-group', \S::get( 'group-id' ) );

    $group_id = \S::get_session( 'finance-group' );

    if ( !$group_id )
    {
      $group_id = $repo -> defaultGroup();
      \S::set_session( 'finance-group', $group_id );
    }

    return $group_id;
  }

  public static function mainView()
  {
    if ( !self::requireAuth() )
      return false;

    $repo = self::repo();
    $importRepo = self::importRepo();
    $importRepo -> ensureTables();

    if ( \S::get( 'tag-clear' ) )
      unset( $_SESSION['finance-tag-id'] );

    if ( \S::get( 'client_clear' ) )
      unset( $_SESSION['finance_client_id'] );

    if ( \S::get( 'dates' ) )
      \S::set_session( 'finance-dates', \S::get( 'dates' ) );

    if ( \S::get( 'tag-id' ) )
      \S::set_session( 'finance-tag-id', \S::get( 'tag-id' ) );

    if ( \S::get( 'client_id' ) )
      \S::set_session( 'finance_client_id', \S::get( 'client_id' ) );

    $group_id = self::resolveGroup( $repo );
    $tag_id = \S::get_session( 'finance-tag-id' );
    $client_id = \S::get_session( 'finance_client_id' );

    list( $date_from, $date_to ) = self::resolveDates();

    if ( $group_id == 3 )
    {
      $date_from = '2000-01-01';
      $date_to = '3000-01-01';
    }

    return \Tpl::view( 'finances/main-view', [
      'categories' => $repo -> categories( $date_from, $date_to, $tag_id, null, $group_id, $client_id ),
      'date_from' => $date_from,
      'date_to' => $date_to,
      'clients' => $repo -> clientsWithRevenue( $date_from, $date_to, $group_id ),
      'client_id' => $client_id,
      'groups' => $repo -> groupsList(),
      'group_id' => $group_id,
      'operations_list' => $repo -> operationsList( $date_from, $date_to, $group_id, $client_id ),
      'wallet_summary' => $repo -> walletSummary( $group_id ),
      'wallet_summary_this_month' => $repo -> walletSummaryThisMonth( $group_id ),
      'wallet_income_this_month' => $repo -> walletIncomeThisMonth( $group_id ),
      'wallet_expenses_this_month' => $repo -> walletExpensesThisMonth( $group_id ),
      'fakturownia_pending_clients' => self::preparePendingRows( $importRepo -> pendingClientMappings() ),
      'fakturownia_pending_items' => self::preparePendingRows( $importRepo -> pendingItemMappings() ),
      'fakturownia_crm_clients' => $repo -> clientsList(),
      'fakturownia_categories' => self::prepareCategoryOptions( $repo -> categoriesFlatList() ),
      'fakturownia_last_summary' => self::lastImportSummary( $importRepo )
    ] );
  }

  private static function preparePendingRows( $rows )
  {
    $output = [];

    if ( !is_array( $rows ) )
      return $output;

    foreach ( $rows as $row )
    {
      $payload = [];
      if ( isset( $row['payload_json'] ) && $row['payload_json'] )
      {
        $decoded = json_decode( $row['payload_json'], true );
        if ( is_array( $decoded ) )
          $payload = $decoded;
      }

      $output[] = [
        'external_key' => (string)$row['external_key'],
        'external_name' => (string)$row['external_name'],
        'hits' => (int)$row['hits'],
        'last_seen_at' => (string)$row['last_seen_at'],
        'payload' => $payload
      ];
    }

    return $output;
  }

  private static function lastImportSummary( $importRepo )
  {
    $raw = $importRepo -> getState( 'last_import_summary' );
    if ( !$raw )
      return null;

    $decoded = json_decode( $raw, true );
    return is_array( $decoded ) ? $decoded : null;
  }

  private static function prepareCategoryOptions( $categories )
  {
    $options = [];
    if ( !is_array( $categories ) || empty( $categories ) )
      return $options;

    $byId = [];
    foreach ( $categories as $category )
    {
      $id = isset( $category['id'] ) ? (int)$category['id'] : 0;
      if ( $id <= 0 )
        continue;

      $byId[ $id ] = $category;
    }

    foreach ( $categories as $category )
    {
      $id = isset( $category['id'] ) ? (int)$category['id'] : 0;
      if ( $id <= 0 )
        continue;

      $label = self::buildCategoryPathLabel( $id, $byId );
      $options[] = [
        'id' => $id,
        'name' => $label,
        'group_id' => isset( $category['group_id'] ) ? (int)$category['group_id'] : 0
      ];
    }

    usort( $options, function( $left, $right )
    {
      return strcmp( $left['name'], $right['name'] );
    } );

    return $options;
  }

  private static function buildCategoryPathLabel( $categoryId, $byId )
  {
    $parts = [];
    $guard = 0;
    $currentId = (int)$categoryId;

    while ( $currentId > 0 && isset( $byId[ $currentId ] ) )
    {
      $category = $byId[ $currentId ];
      $name = trim( (string)( $category['name'] ?? '' ) );
      if ( $name !== '' )
        array_unshift( $parts, $name );

      $parentId = isset( $category['parent_id'] ) ? (int)$category['parent_id'] : 0;
      if ( $parentId <= 0 || $parentId === $currentId )
        break;

      $currentId = $parentId;
      $guard++;
      if ( $guard > 20 )
        break;
    }

    if ( empty( $parts ) )
      return 'Kategoria #' . (int)$categoryId;

    return implode( ' > ', $parts );
  }

  public static function operationEdit()
  {
    if ( !self::requireAuth() )
      return false;

    $repo = self::repo();

    $operationId = (int)\S::get( 'operation-id' );
    $fakturowniaContext = null;

    if ( $operationId > 0 )
      $fakturowniaContext = $repo -> fakturowniaOperationContext( $operationId );

    return \Tpl::view( 'finances/operation-edit', [
      'operation' => $repo -> operationDetails( \S::get( 'operation-id' ) ),
      'category_id' => \S::get( 'category-id' ),
      'tags' => $repo -> tagsList( \S::get_session( 'finance-group' ) ),
      'tags_json' => $repo -> tagsJson( \S::get_session( 'finance-group' ) ),
      'operation_date' => \S::get_session( 'operation-date' ),
      'clients' => $repo -> clientsList(),
      'fakturownia_operation_context' => $fakturowniaContext,
      'fakturownia_categories' => self::prepareCategoryOptions( $repo -> categoriesFlatList() )
    ] );
  }

  public static function operationSave()
  {
    if ( !self::requireAuth() )
      return false;

    $repo = self::repo();
    $response = [ 'status' => 'error', 'msg' => 'Podczas zapisywania operacji wystąpił błąd. Proszę spróbować ponownie.' ];
    $values = \S::json_to_array( \S::get( 'values' ) );

    \S::set_session( 'operation-date', $values['date'] );

    $client_id = isset( $values['client_id'] ) ? (int)$values['client_id'] : null;

    if ( $id = $repo -> saveOperation(
      $values['id'], $values['category_id'], $values['date'], $values['amount'],
      $values['description'], $values['tags'], \S::get_session( 'finance-group' ), $client_id
    ) )
      $response = [ 'status' => 'ok', 'msg' => 'Operacja została zapisana.', 'id' => $id ];

    echo json_encode( $response );
    exit;
  }

  public static function operationDelete()
  {
    if ( !self::requireAuth() )
      return false;

    $repo = self::repo();

    if ( $repo -> deleteOperation( \S::get( 'operation-id' ) ) )
    {
      \S::alert( 'Wybrana operacja została usunięta.' );
      header( 'Location: /finances/operations_list/category-id=' . \S::get( 'category-id' ) );
      exit;
    }
  }

  public static function categoryEdit()
  {
    if ( !self::requireAuth() )
      return false;

    $repo = self::repo();

    return \Tpl::view( 'finances/category-edit', [
      'category' => $repo -> categoryDetails( \S::get( 'id' ) ),
      'group_id' => \S::get_session( 'finance-group' )
    ] );
  }

  public static function categorySave()
  {
    if ( !self::requireAuth() )
      return false;

    $repo = self::repo();
    $response = [ 'status' => 'error', 'msg' => 'Podczas zapisywania kategorii wystąpił błąd. Proszę spróbować ponownie.' ];
    $values = \S::json_to_array( \S::get( 'values' ) );

    if ( $id = $repo -> saveCategory(
      $values['id'], $values['name'], $values['parent_id'], $values['group_id']
    ) )
      $response = [ 'status' => 'ok', 'msg' => 'Kategoria została zapisana.', 'id' => $id ];

    echo json_encode( $response );
    exit;
  }

  public static function categoryDelete()
  {
    if ( !self::requireAuth() )
      return false;

    $repo = self::repo();

    if ( $repo -> deleteCategory( \S::get( 'category_id' ) ) )
    {
      \S::alert( 'Wybrana kategoria została usunięta.' );
      header( 'Location: /finances/main_view/' );
      exit;
    }
  }

  public static function operationsList()
  {
    if ( !self::requireAuth() )
      return false;

    $repo = self::repo();
    $dates = \S::get( 'dates' );

    if ( $dates )
      \S::set_session( 'finance-dates', $dates );

    $dates = \S::get_session( 'finance-dates' );
    $dates = explode( ' - ', $dates );

    $date_from = $dates[0] ? $dates[0] : date( 'Y-m-01' );
    $date_to = $dates[1] ? $dates[1] : date( 'Y-m-t' );

    $category = $repo -> categoryDetails( \S::get( 'category-id' ) );

    if ( $category['group_id'] == 3 )
    {
      $date_from = '2000-01-01';
      $date_to = '3000-01-01';
    }

    return \Tpl::view( 'finances/operations-list', [
      'category' => $category,
      'operations' => $repo -> operations( \S::get( 'category-id' ), $date_from, $date_to, \S::get_session( 'finance-tag-id' ) ),
      'date_from' => $date_from,
      'date_to' => $date_to
    ] );
  }

  public static function fakturowniaClientMappingSave()
  {
    if ( !self::requireAuth() )
      return false;

    if ( !\S::csrf_verify() )
    {
      \S::alert( 'Nieprawidlowy token bezpieczenstwa. Odswiez strone i sproboj ponownie.' );
      header( 'Location: /finances/main_view/' );
      exit;
    }

    $externalKey = trim( (string)\S::get( 'external_key' ) );
    $externalName = trim( (string)\S::get( 'external_name' ) );
    $crmClientId = (int)\S::get( 'crm_client_id' );
    $repo = self::repo();

    if ( $externalKey === '' || $externalName === '' || $crmClientId <= 0 || !$repo -> clientExists( $crmClientId ) )
    {
      \S::alert( 'Nie udalo sie zapisac mapowania klienta. Uzupelnij wszystkie pola.' );
      header( 'Location: /finances/main_view/' );
      exit;
    }

    $importRepo = self::importRepo();
    $importRepo -> ensureTables();
    $importRepo -> saveClientMapping( $externalKey, $externalName, $crmClientId );

    \S::alert( 'Mapowanie klienta zostalo zapisane.' );
    header( 'Location: /finances/main_view/' );
    exit;
  }

  public static function fakturowniaItemMappingSave()
  {
    if ( !self::requireAuth() )
      return false;

    if ( !\S::csrf_verify() )
    {
      \S::alert( 'Nieprawidlowy token bezpieczenstwa. Odswiez strone i sproboj ponownie.' );
      header( 'Location: /finances/main_view/' );
      exit;
    }

    $externalKey = trim( (string)\S::get( 'external_key' ) );
    $externalName = trim( (string)\S::get( 'external_name' ) );
    $financeCategoryId = (int)\S::get( 'finance_category_id' );
    $repo = self::repo();

    if ( $externalKey === '' || $externalName === '' || $financeCategoryId <= 0 || !$repo -> categoryExists( $financeCategoryId ) )
    {
      \S::alert( 'Nie udalo sie zapisac mapowania pozycji. Uzupelnij wszystkie pola.' );
      header( 'Location: /finances/main_view/' );
      exit;
    }

    $importRepo = self::importRepo();
    $importRepo -> ensureTables();
    $importRepo -> saveItemMapping( $externalKey, $externalName, $financeCategoryId );

    \S::alert( 'Mapowanie pozycji zostalo zapisane.' );
    header( 'Location: /finances/main_view/' );
    exit;
  }

  public static function fakturowniaDocumentPositionSkip()
  {
    if ( !self::requireAuth() )
      return false;

    if ( !\S::csrf_verify() )
    {
      \S::alert( 'Nieprawidlowy token bezpieczenstwa. Odswiez strone i sproboj ponownie.' );
      header( 'Location: /finances/main_view/' );
      exit;
    }

    $externalId = trim( (string)\S::get( 'external_id' ) );
    $documentType = trim( (string)\S::get( 'document_type' ) );
    $externalItemKey = trim( (string)\S::get( 'external_item_key' ) );
    $itemName = trim( (string)\S::get( 'item_name' ) );

    if ( $externalId === '' || $documentType === '' || $externalItemKey === '' )
    {
      \S::alert( 'Nie udalo sie pominac pozycji — brakuje danych faktury.' );
      header( 'Location: /finances/main_view/' );
      exit;
    }

    $importRepo = self::importRepo();
    $importRepo -> ensureTables();
    $importRepo -> markDocumentPositionSkipped( $externalId, $documentType, $externalItemKey, $itemName );
    $importRepo -> removeOccurrenceFromItemQueue( $externalItemKey, $externalId );

    \S::alert( 'Pozycja zostala pominieta tylko dla faktury ' . $externalId . '.' );
    header( 'Location: /finances/main_view/' );
    exit;
  }

  public static function fakturowniaOperationMappingSave()
  {
    if ( !self::requireAuth() )
      return false;

    if ( !\S::csrf_verify() )
    {
      \S::alert( 'Nieprawidlowy token bezpieczenstwa. Odswiez strone i sproboj ponownie.' );
      header( 'Location: /finances/main_view/' );
      exit;
    }

    $operationId = (int)\S::get( 'operation_id' );
    $financeCategoryId = (int)\S::get( 'finance_category_id' );
    $applyToAll = (int)\S::get( 'apply_to_all' ) === 1;
    $returnCategoryId = (int)\S::get( 'return_category_id' );

    $repo = self::repo();
    if ( $operationId <= 0 || $financeCategoryId <= 0 || !$repo -> categoryExists( $financeCategoryId ) )
    {
      \S::alert( 'Nie udalo sie zapisac dopasowania. Sprawdz dane formularza.' );
      header( 'Location: /finances/main_view/' );
      exit;
    }

    $context = $repo -> fakturowniaOperationContext( $operationId );
    if ( !$context )
    {
      \S::alert( 'Ta operacja nie jest powiazana z mapowaniem Fakturownia.' );
      header( 'Location: /finances/operations_list/category-id=' . $returnCategoryId );
      exit;
    }

    $importRepo = self::importRepo();
    $importRepo -> ensureTables();
    $importRepo -> saveItemMapping( $context['external_item_key'], $context['external_name'], $financeCategoryId );

    $repo -> updateOperationCategory( $operationId, $financeCategoryId );
    $updatedCount = $repo -> updateFakturowniaOperationsCategoryByItemName( $context['item_name'], $financeCategoryId, $applyToAll );

    if ( $applyToAll )
      \S::alert( 'Dopasowanie zmienione. Zaktualizowano operacje: ' . (int)$updatedCount . '.' );
    else
      \S::alert( 'Dopasowanie zmienione dla tej operacji.' );

    header( 'Location: /finances/operations_list/category-id=' . $financeCategoryId );
    exit;
  }
}
