<?php
/**
 * /backreload/v1/destinations endpoints.
 *
 * @package Backreload\Api
 */

namespace Backreload\Api;

use Backreload\Models\DestinationRecord;
use Backreload\Destinations\ValidationException;
use WP_REST_Request;

if ( ! defined( 'ABSPATH' ) ) { exit; }

class DestinationsController extends BaseController {

	public function register( string $namespace ): void {
		register_rest_route( $namespace, '/destinations', [
			[ 'methods' => 'GET',  'callback' => [ $this, 'index' ],  'permission_callback' => [ $this, 'check_perm' ] ],
			[ 'methods' => 'POST', 'callback' => [ $this, 'create' ], 'permission_callback' => [ $this, 'check_perm' ] ],
		] );

		register_rest_route( $namespace, '/destinations/catalog', [
			'methods'             => 'GET',
			'callback'            => [ $this, 'catalog' ],
			'permission_callback' => [ $this, 'check_perm' ],
		] );

		register_rest_route( $namespace, '/destinations/(?P<id>[a-zA-Z0-9_-]+)', [
			[ 'methods' => 'GET',    'callback' => [ $this, 'show' ],    'permission_callback' => [ $this, 'check_perm' ] ],
			[ 'methods' => 'PATCH',  'callback' => [ $this, 'update' ],  'permission_callback' => [ $this, 'check_perm' ] ],
			[ 'methods' => 'DELETE', 'callback' => [ $this, 'destroy' ], 'permission_callback' => [ $this, 'check_perm' ] ],
		] );

		register_rest_route( $namespace, '/destinations/(?P<id>[a-zA-Z0-9_-]+)/test', [
			'methods'             => 'POST',
			'callback'            => [ $this, 'test' ],
			'permission_callback' => [ $this, 'check_perm' ],
		] );

		// Toggle the "skip me for now" flag — narrow endpoint so the UI can
		// flip it with one click without sending a full config patch.
		register_rest_route( $namespace, '/destinations/(?P<id>[a-zA-Z0-9_-]+)/pause', [
			'methods'             => 'POST',
			'callback'            => [ $this, 'pause' ],
			'permission_callback' => [ $this, 'check_perm' ],
		] );
		register_rest_route( $namespace, '/destinations/(?P<id>[a-zA-Z0-9_-]+)/resume', [
			'methods'             => 'POST',
			'callback'            => [ $this, 'resume' ],
			'permission_callback' => [ $this, 'check_perm' ],
		] );
	}

	public function pause( WP_REST_Request $req ) {
		return $this->set_paused( (string) $req['id'], true );
	}

	public function resume( WP_REST_Request $req ) {
		return $this->set_paused( (string) $req['id'], false );
	}

	private function set_paused( string $id, bool $paused ) {
		$d = DestinationRecord::find( $id );
		if ( ! $d ) {
			return $this->error( 'not_found', 'Destination not found', 404 );
		}
		$d->paused = $paused;
		$d->save();
		$this->plugin->get( 'logger' )->info(
			( $paused ? 'Paused' : 'Resumed' ) . " destination {$d->name}"
		);
		return $this->ok( $d->to_array() );
	}

	public function index() {
		$items = [];
		foreach ( DestinationRecord::all() as $d ) {
			$arr = $d->to_array();
			// Augment with live usage
			try {
				$handler = $this->plugin->get( 'destinations' )->handler( $d->type );
				$arr['usage'] = $handler->usage( $d );
			} catch ( \Throwable $e ) {
				$arr['usage'] = [ 'used' => 0, 'free' => 0, 'total' => 0 ];
			}
			$items[] = $arr;
		}
		return $this->ok( [ 'items' => $items ] );
	}

	public function catalog() {
		return $this->ok( [ 'items' => $this->plugin->get( 'destinations' )->catalog() ] );
	}

	public function show( WP_REST_Request $req ) {
		$d = DestinationRecord::find( (string) $req['id'] );
		if ( ! $d ) {
			return $this->error( 'not_found', 'Destination not found', 404 );
		}
		return $this->ok( $d->to_array() );
	}

	public function create( WP_REST_Request $req ) {
		$type    = (string) $req->get_param( 'type' );
		$name    = (string) $req->get_param( 'name' );
		$config  = (array)  $req->get_param( 'config' );
		$manager = $this->plugin->get( 'destinations' );

		if ( ! $manager->has( $type ) ) {
			return $this->error( 'bad_request', "Unknown destination type: {$type}", 400 );
		}

		try {
			$config = $manager->handler( $type )->validate( $config );
		} catch ( ValidationException $e ) {
			return $this->error( 'validation_failed', 'Invalid configuration', 422, [ 'errors' => $e->errors() ] );
		}

		$d = DestinationRecord::create( [
			'type'   => $type,
			'name'   => $name ?: ucfirst( $type ),
			'config' => $config,
			'status' => 'connected',
		] );
		return $this->ok( $d->to_array(), 201 );
	}

	public function update( WP_REST_Request $req ) {
		$d = DestinationRecord::find( (string) $req['id'] );
		if ( ! $d ) {
			return $this->error( 'not_found', 'Destination not found', 404 );
		}
		if ( $name = $req->get_param( 'name' ) ) {
			$d->name = (string) $name;
		}
		if ( null !== $req->get_param( 'config' ) ) {
			// Drop empty SECRET fields from the incoming config so the merge
			// preserves the previously-saved value. Without this an "edit"
			// flow where the user leaves the password blank would overwrite
			// the stored credential with an empty string and then fail
			// "required" validation.
			$incoming = (array) $req->get_param( 'config' );
			foreach ( $incoming as $k => $v ) {
				if ( is_string( $v ) && '' === $v && \Backreload\Core\Encryption::is_secret_field( (string) $k ) ) {
					unset( $incoming[ $k ] );
				}
			}
			try {
				$d->config = $this->plugin->get( 'destinations' )->handler( $d->type )
					->validate( array_merge( $d->config, $incoming ) );
			} catch ( ValidationException $e ) {
				return $this->error( 'validation_failed', 'Invalid configuration', 422, [ 'errors' => $e->errors() ] );
			}
		}
		$d->save();
		return $this->ok( $d->to_array() );
	}

	public function destroy( WP_REST_Request $req ) {
		$d = DestinationRecord::find( (string) $req['id'] );
		if ( ! $d ) {
			return $this->error( 'not_found', 'Destination not found', 404 );
		}
		$d->delete();
		return $this->ok( [ 'deleted' => true ] );
	}

	public function test( WP_REST_Request $req ) {
		$d = DestinationRecord::find( (string) $req['id'] );
		if ( ! $d ) {
			return $this->error( 'not_found', 'Destination not found', 404 );
		}
		try {
			$ok = $this->plugin->get( 'destinations' )->handler( $d->type )->test( $d );
			$d->status      = $ok ? 'connected' : 'error';
			$d->last_tested = current_time( 'mysql', true );
			$d->save();
			return $this->ok( [ 'ok' => $ok ] );
		} catch ( \Throwable $e ) {
			$d->status = 'error';
			$d->save();
			// Log the FULL exception so an admin debugging from the Logs page
			// has the actual cause (bucket name, host, auth-failure reason,
			// etc.). Don't echo it to the REST response: those messages can
			// reveal internal IPs (FTP PASV: "227 ... 10.0.0.5,12,34..."),
			// confirm bucket existence (S3 "NoSuchBucket: example-backup"),
			// or leak auth details to anyone who finds an admin XSS. Surface
			// a generic message to the UI and let the admin look up the real
			// reason in Logs.
			$this->plugin->get( 'logger' )->error(
				'Destination test failed: ' . $e->getMessage(),
				[ 'destination_id' => $d->ext_id, 'destination_type' => $d->type ]
			);
			return $this->error(
				'test_failed',
				'Connection failed. See plugin logs for details.',
				500
			);
		}
	}
}
