use std::sync::Arc;
use std::thread;
use std::thread::JoinHandle;

use crossbeam::channel;
use crossbeam::channel::{Receiver, Sender};

use crate::concurrency::Semaphore;
use crate::duplicate_detection::messages::DuplicateInvestigation;

pub struct OpenFileGuard {
    request_in: Receiver<DuplicateInvestigation>,
    response_out: Sender<DuplicateInvestigation>,
    sem: Arc<Semaphore>,
}

impl OpenFileGuard {
    pub fn new(
        sem: Arc<Semaphore>,
    ) -> (
        Self,
        Sender<DuplicateInvestigation>,
        Receiver<DuplicateInvestigation>,
    ) {
        let (request_out, request_in) = channel::unbounded();
        let (response_out, response_in) = channel::unbounded();

        (
            OpenFileGuard {
                request_in,
                response_out,
                sem,
            },
            request_out,
            response_in,
        )
    }

    pub fn work(self) -> JoinHandle<::anyhow::Result<Self>> {
        thread::spawn(move || {
            info!("Mutex guard started");
            self.work_internal()?;
            info!("Mutex guard ended");
            Ok(self)
        })
    }

    fn work_internal(&self) -> ::anyhow::Result<()> {
        while let Ok(investigation) = self.request_in.recv() {
            let work_size = investigation.duplicate.locations.len();
            debug!("Requesting {} leases", work_size);
            // If we have more than 512 files to open in a single batch of work,
            // try to claim all handles at once
            if work_size > 512 {
                self.sem.acquire(512);
            } else {
                self.sem.acquire(work_size);
            }

            debug!("Leases granted");

            self.response_out.send(investigation)?;
        }
        Ok(())
    }
}
